From f0d104b32af8441a865fd3c45d47af6599e5f6c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20B=C3=BCchner?= Date: Tue, 25 Feb 2025 14:00:31 +0100 Subject: [PATCH] apply fixes from https://github.com/borrougagnou/maintenance-termbin/ --- fiche.c | 203 ++++++++++++++++++++++++++------------------------------ 1 file changed, 93 insertions(+), 110 deletions(-) diff --git a/fiche.c b/fiche.c index 8eeedcd..6fcd3d8 100644 --- a/fiche.c +++ b/fiche.c @@ -33,7 +33,9 @@ $ cat fiche.c | nc localhost 9999 #include #include +#include #include +#include #include #include #include @@ -105,10 +107,9 @@ static void dispatch_connection(int socket, Fiche_Settings *settings); */ static void *handle_connection(void *args); -char* replace_substr(const char *str, const char *old, const char *new); - // Server-related utils + /** * @brief Generates a slug that will be used for paste creation * @warning output has to be freed after using! @@ -128,7 +129,7 @@ static void generate_slug(char **output, uint8_t length, uint8_t extra_length); /** * @brief Creates a directory at requested path using requested slug - * @returns 0 if succeded, 1 if failed or dir already existed + * @returns 0 if succeeded, 1 if failed or dir already existed * * @arg output_dir root directory for all pastes * @arg slug directory name for a particular paste @@ -253,19 +254,29 @@ int fiche_run(Fiche_Settings settings) { } } - // Check if log file is writable (if set) + // Check if log file is valid and writable (if set) if ( settings.log_file_path ) { - // Create log file if it doesn't exist - FILE *f = fopen(settings.log_file_path, "a+"); - fclose(f); + struct stat log_file_st; + memset(&log_file_st, 0, sizeof(struct stat)); - // Then check if it's accessible - if ( access(settings.log_file_path, W_OK) != 0 ) { - print_error("Log file not writable!"); - return -1; + if ( stat(settings.log_file_path, &log_file_st) == 0 ) { + // Is the log file a regular file? + if ( !S_ISREG(log_file_st.st_mode) ) { + print_error("Log file is not valid!"); + return -1; + } + + // Can we write to it? + if ( access(settings.log_file_path, W_OK) != 0 ) { + print_error("Log file is not writable!"); + return -1; + } + } else { + // Log file doesn't exist - create it. + FILE *f = fopen(settings.log_file_path, "a+"); + fclose(f); } - } // Try to set domain name @@ -279,7 +290,7 @@ int fiche_run(Fiche_Settings settings) { // Perform final cleanup - // This is allways allocated on the heap + // This is always allocated on the heap free(settings.domain); return 0; @@ -405,22 +416,38 @@ static int perform_user_change(const Fiche_Settings *settings) { // Get user details const struct passwd *userdata = getpwnam(settings->user_name); - const int uid = userdata->pw_uid; - const int gid = userdata->pw_gid; - - if (uid == -1 || gid == -1) { + if (!userdata) { print_error("Could find requested user: %s!", settings->user_name); return -1; } + const uid_t uid = userdata->pw_uid; + const gid_t gid = userdata->pw_gid; + if (setgid(gid) != 0) { - print_error("Couldn't switch to requested user: %s!", settings->user_name); + print_error("Couldn't switch to requested group for user: %s!", settings->user_name); + } + + // Check if gid change actually happened. + if (getgid() != gid) { + print_error("Couldn't switch to requested group for user: %s!", settings->user_name); + } + + // We must re-initialize supplementary groups to avoid inheriting + // root's supplementary groups. + if (initgroups(settings->user_name, gid) != 0) { + print_error("Couldn't initialize supplementary groups for user: %s!", settings->user_name); } if (setuid(uid) != 0) { print_error("Couldn't switch to requested user: %s!", settings->user_name); } + // Check if uid change actually happened. + if (getuid() != uid) { + print_error("Couldn't switch to requested user: %s!", settings->user_name); + } + print_status("User changed to: %s.", settings->user_name); return 0; @@ -442,28 +469,20 @@ static int start_server(Fiche_Settings *settings) { return -1; } - // Prepare address and port handler for IPv6 + // Prepare address and port handler struct sockaddr_in6 address; address.sin6_family = AF_INET6; - address.sin6_addr = in6addr_any; // Bind to any address, for a specific address use inet_pton + inet_pton(AF_INET6, settings->listen_addr, &address.sin6_addr); address.sin6_port = htons(settings->port); - // Convert IPv4 address to IPv6 if needed - if (settings->listen_addr) { - if (inet_pton(AF_INET6, settings->listen_addr, &address.sin6_addr) != 1) { - print_error("Invalid IPv6 address!"); - return -1; - } - } - // Bind to port - if (bind(s, (struct sockaddr *) &address, sizeof(address)) != 0) { + if ( bind(s, (struct sockaddr *) &address, sizeof(address)) != 0) { print_error("Couldn't bind to the port: %d!", settings->port); return -1; } // Start listening - if (listen(s, 128) != 0) { + if ( listen(s, 128) != 0 ) { print_error("Couldn't start listening on the socket!"); return -1; } @@ -480,7 +499,7 @@ static int start_server(Fiche_Settings *settings) { // Give some time for all threads to finish // NOTE: this code is reached only in testing environment // There is currently no way to kill the main thread from any thread - // Something like this can be done for testing purpouses: + // Something like this can be done for testing purposes: // int i = 0; // while (i < 3) { // dispatch_connection(s, settings); @@ -498,13 +517,14 @@ static void dispatch_connection(int socket, Fiche_Settings *settings) { // Create address structs for this socket struct sockaddr_in6 address; socklen_t addlen = sizeof(address); - - // Accept a connection and get a new socket id + + // Accept a connection and get a new socket id const int s = accept(socket, (struct sockaddr *) &address, &addlen); if (s < 0 ) { print_error("Error on accepting connection!"); return; } + // Set timeout for accepted socket const struct timeval timeout = { 5, 0 }; @@ -528,70 +548,42 @@ static void dispatch_connection(int socket, Fiche_Settings *settings) { // Spawn a new thread to handle this connection pthread_t id; + pthread_attr_t attr; - if ( pthread_create(&id, NULL, &handle_connection, c) != 0 ) { + if ( (errno = pthread_attr_init(&attr)) || + (errno = pthread_attr_setstacksize(&attr, 128*1024)) || + (errno = pthread_create(&id, &attr, &handle_connection, c)) ) { + pthread_attr_destroy(&attr); print_error("Couldn't spawn a thread!"); return; } - // Detach thread if created succesfully + pthread_attr_destroy(&attr); + + // Detach thread if created successfully // TODO: consider using pthread_tryjoin_np pthread_detach(id); } -char* replace_substr(const char *str, const char *old, const char *new) { - char *result; - int i, count = 0; - int newlen = strlen(new); - int oldlen = strlen(old); - - // Counting the number of times the old substring occurs in the string - for (i = 0; str[i] != '\0'; i++) { - if (strstr(&str[i], old) == &str[i]) { - count++; - i += oldlen - 1; - } - } - - // Allocating memory for the new string - result = (char *)malloc(i + count * (newlen - oldlen) + 1); - - i = 0; - while (*str) { - // Compare the substring with the result - if (strstr(str, old) == str) { - strcpy(&result[i], new); - i += newlen; - str += oldlen; - } else { - result[i++] = *str++; - } - } - - result[i] = '\0'; - return result; -} - static void *handle_connection(void *args) { - + char *slug = NULL; + uint8_t *buffer = NULL; // Cast args to it's previous type struct fiche_connection *c = (struct fiche_connection *) args; - //struct sockaddr_in6 *addr = (struct sockaddr_in6 *)&c->address; - char ipstr[INET6_ADDRSTRLEN]; - char *ip; - - struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&c->address; - inet_ntop(AF_INET6, &addr6->sin6_addr, ipstr, sizeof(ipstr)); - ip = replace_substr(ipstr, "::ffff:", ""); + // Get client's IP + char ip_str[INET6_ADDRSTRLEN]; + const char *ip = inet_ntop(AF_INET6, &c->address.sin6_addr, ip_str, INET6_ADDRSTRLEN); // Get client's hostname char hostname[1024]; + if (getnameinfo((struct sockaddr *)&c->address, sizeof(c->address), hostname, sizeof(hostname), NULL, 0, 0) != 0 ) { + // Couldn't resolve a hostname strcpy(hostname, "n/a"); } @@ -601,30 +593,37 @@ static void *handle_connection(void *args) { char date[64]; get_date(date); print_status("%s", date); + print_status("Incoming connection from: %s (%s).", ip, hostname); } // Create a buffer - uint8_t buffer[c->settings->buffer_len]; - memset(buffer, 0, c->settings->buffer_len); + buffer = calloc(c->settings->buffer_len, 1); + if (!buffer) { + print_error("Couldn't allocate the buffer!"); + print_separator(); - const int r = recv(c->socket, buffer, sizeof(buffer), MSG_WAITALL); + goto exit; + } + + const int r = recv(c->socket, buffer, c->settings->buffer_len, MSG_WAITALL); if (r <= 0) { print_error("No data received from the client!"); print_separator(); - // Close the socket - close(c->socket); - - // Cleanup - free(c); - free(ip); - pthread_exit(NULL); - - return 0; + goto exit; } - char *slug; + // - Check if request was performed with a known protocol + // TODO + + // - Check if on whitelist + // TODO + + // - Check if on banlist + // TODO + + // Generate slug and use it to create an url uint8_t extra = 0; do { @@ -648,12 +647,7 @@ static void *handle_connection(void *args) { print_error("Couldn't generate a valid slug!"); print_separator(); - // Cleanup - close(c->socket); - free(c); - free(slug); - pthread_exit(NULL); - return NULL; + goto exit; } } @@ -665,12 +659,7 @@ static void *handle_connection(void *args) { print_error("Couldn't generate a slug!"); print_separator(); - close(c->socket); - - // Cleanup - free(c); - pthread_exit(NULL); - return NULL; + goto exit; } @@ -679,13 +668,7 @@ static void *handle_connection(void *args) { print_error("Couldn't save a file!"); print_separator(); - close(c->socket); - - // Cleanup - free(c); - free(slug); - pthread_exit(NULL); - return NULL; + goto exit; } // Write a response to the user @@ -707,15 +690,15 @@ static void *handle_connection(void *args) { // TODO: log unsuccessful and rejected connections log_entry(c->settings, ip, hostname, slug); +exit: // Close the connection close(c->socket); // Perform cleanup of values used in this thread + free(buffer); free(slug); free(c); - pthread_exit(NULL); - return NULL; }