/* Hiawatha | session.c * * All the routines to handle a session. A session is a connection from a * client to the webserver. */ #include "config.h" #include #include #include #include #include #include #include #include "alternative.h" #include "libstr.h" #include "liblist.h" #include "session.h" #ifdef HAVE_COMMAND #include "command.h" #endif int new_client_id = 0; /* Set the entries in a session-record to the default values. */ static void clear_session(t_session *session) { session->head_request = false; session->cgi_type = none; session->cgi_handler = NULL; session->fcgi_server = NULL; session->method = NULL; session->uri = NULL; session->uri_len = 0; session->uri_is_dir = false; #ifdef HAVE_REWRITE session->script_url = NULL; #endif session->accept_gzip = false; session->encode_gzip = false; session->path_info = NULL; session->alias_used = false; session->vars = NULL; session->http_version = NULL; session->headerfields = NULL; session->body = NULL; session->local_user = NULL; session->data_sent = false; #ifdef HAVE_SSL session->cause_of_301 = missing_slash; #endif session->header_length = 0; session->content_length = 0; session->file_on_disk = NULL; session->mimetype = NULL; session->hostname = NULL; session->host = session->config->first_host; session->host_copied = false; session->throttle = 0; session->throttle_timer = 0; session->bytecounter = 0; session->part_of_dirspeed = false; session->remote_user = NULL; session->directory = NULL; session->handling_error = false; session->reason_for_403 = ""; session->cookie = NULL; session->bytes_sent = 0; session->output_size = 0; session->return_code = 200; session->error_code = -1; session->tempdata = NULL; session->put_method = false; session->uploaded_file = NULL; session->uploaded_size = 0; #ifdef DEBUG session->status = NULL; #endif } /* Initialize a session-record. */ void init_session(t_session *session) { #ifdef HAVE_COMMAND increment_counter(COUNTER_CLIENT); #endif if ((session->client_id = new_client_id++) == MAX_CHILD_ID) { new_client_id = 0; } session->force_quit = false; session->kept_alive = 0; #ifdef HAVE_SSL session->ssl_data = NULL; #endif session->request = NULL; session->buffer_size = 0; session->bytes_in_buffer = 0; session->flooding_timer = time(NULL); session->ip_resolved = false; clear_session(session); } /* Reset a session-record for reuse. */ void reset_session(t_session *session) { int size; sfree(session->file_on_disk); sfree(session->local_user); sfree(session->remote_user); sfree(session->path_info); #ifdef HAVE_REWRITE sfree(session->script_url); #endif if (session->uploaded_file != NULL) { unlink(session->uploaded_file); free(session->uploaded_file); } #ifdef DEBUG sfree(session->status); #endif remove_headerfields(session); if (session->directory != NULL) { pthread_mutex_lock(&(session->directory->client_mutex)); if (session->part_of_dirspeed) { if (--session->directory->nr_of_clients == 0) { session->directory->session_speed = session->directory->upload_speed; } else { session->directory->session_speed = session->directory->upload_speed / session->directory->nr_of_clients; } } pthread_mutex_unlock(&(session->directory->client_mutex)); } /* HTTP pipelining */ size = session->header_length + session->content_length; if ((session->bytes_in_buffer > size) && session->keep_alive) { session->bytes_in_buffer -= size; memmove(session->request, session->request + size, session->bytes_in_buffer); *(session->request + session->bytes_in_buffer) = '\0'; } else { sfree(session->request); session->request = NULL; session->buffer_size = 0; session->bytes_in_buffer = 0; } remove_tempdata(session->tempdata); if (session->host_copied) { free(session->host); } clear_session(session); } /* Free all remaining buffers */ void destroy_session(t_session *session) { sfree(session->request); } /* Parse the HTTP headerfields from the received request. */ t_headerfield *parse_headerfields(char *line) { t_headerfield *first, *headerfield; char *value; if (*line == '\0') { return NULL; } if ((first = headerfield = (t_headerfield*)malloc(sizeof(t_headerfield))) == NULL) { return NULL; } headerfield->data = line; headerfield->next = NULL; while (*line != '\0') { if (*line == '\r') { *line = '\0'; if (*(line + 1) == '\n') { if ((*(line + 2) != '\r') && (*(line + 2) != '\0')) { if ((headerfield->next = (t_headerfield*)malloc(sizeof(t_headerfield))) == NULL) { return first; } headerfield = headerfield->next; headerfield->next = NULL; headerfield->data = line + 2; } else { break; } } } line++; } headerfield = first; while (headerfield != NULL) { if ((value = strchr(headerfield->data, ':')) != NULL) { do { value++; } while ((*value == ' ') && (*value != '\0')); headerfield->value_offset = (value - headerfield->data); } else { headerfield->value_offset = 0; } headerfield = headerfield->next; } return first; } /* Search for a headerfield and return its value. */ char *get_headerfield(char *key, t_headerfield *headerfields) { char *retval = NULL; int len; len = strlen(key); while (headerfields != NULL) { if (strncasecmp(headerfields->data, key, len) == 0) { retval = headerfields->data + headerfields->value_offset; break; } headerfields = headerfields->next; } return retval; } /* free() a list of headerfields. */ void remove_headerfields(t_session *session) { t_headerfield *to_be_removed; while (session->headerfields != NULL) { to_be_removed = session->headerfields; session->headerfields = session->headerfields->next; free(to_be_removed); } } /* Return the path of the user's homedirectory. */ int get_homedir(t_session *session, char *username) { struct passwd *pwd; int len; char *old_root; if ((pwd = getpwnam(username)) != NULL) { len = strlen(pwd->pw_dir); old_root = session->host->website_root; if ((session->host->website_root = (char*)malloc(len + strlen(session->config->user_directory) + 2)) == NULL) { session->host->website_root = old_root; return -1; } memcpy(session->host->website_root, pwd->pw_dir, len); *(session->host->website_root + len) = '/'; strcpy(session->host->website_root + len + 1, session->config->user_directory); if (register_tempdata(&(session->tempdata), session->host->website_root, tc_data) == -1) { free(session->host->website_root); session->host->website_root = old_root; return -1; } session->host->website_root_len = strlen(session->host->website_root); return 200; } return 404; } /* Dupliacte the active host-record. The duplicate can now savely be altered * and will be used during the session. */ bool duplicate_host(t_session *session) { t_host *new_host; if ((session->host != NULL) && (session->host_copied == false)) { if ((new_host = (t_host*)malloc(sizeof(t_host))) != NULL) { memcpy(new_host, session->host, sizeof(t_host)); new_host->next = NULL; session->host = new_host; session->host_copied = true; } else { return false; } } return true; } /* Is the requested file marked as volatile? */ bool is_volatile_object(t_session *session) { int i; for (i = 0; i < session->host->volatile_object.size; i++) { if (strcmp(session->file_on_disk, *(session->host->volatile_object.item + i)) == 0) { return true; } } return false; } /* Copy the settings from a directory-record to the active host-record. */ int copy_directory_settings(t_session *session) { size_t path_length; t_directory *dir; bool match; dir = session->config->directory; while (dir != NULL) { path_length = strlen(dir->path); if (strlen(session->file_on_disk) >= path_length) { if (dir->path_match == root) { match = (strncmp(session->file_on_disk, dir->path, path_length) == 0); } else { match = (strstr(session->file_on_disk, dir->path) != NULL); } if (match) { if (duplicate_host(session)) { session->directory = dir; if (dir->max_clients > -1) { pthread_mutex_lock(&(dir->client_mutex)); if (dir->nr_of_clients < dir->max_clients) { session->throttle = dir->session_speed = dir->upload_speed / ++dir->nr_of_clients; pthread_mutex_unlock(&(dir->client_mutex)); session->part_of_dirspeed = true; } else { pthread_mutex_unlock(&(dir->client_mutex)); return 503; } } if (dir->wrap_cgi != NULL) { session->host->wrap_cgi = dir->wrap_cgi; } if (dir->start_file != NULL) { session->host->start_file = dir->start_file; } if (dir->execute_cgiset) { session->host->execute_cgi = dir->execute_cgi; } if (dir->show_index_set) { session->host->show_index = dir->show_index; } if (dir->follow_symlinks_set) { session->host->follow_symlinks = dir->follow_symlinks; } if (dir->use_gz_file_set) { session->host->use_gz_file = dir->use_gz_file; } if (dir->access_list != NULL) { session->host->access_list = dir->access_list; } if (dir->alter_list != NULL) { session->host->alter_list = dir->alter_list; } if (dir->alter_fmode != 0) { session->host->alter_fmode = dir->alter_fmode; } if (dir->image_referer.size > 0) { session->host->image_referer.size = dir->image_referer.size; session->host->image_referer.item = dir->image_referer.item; session->host->imgref_replacement = dir->imgref_replacement; } if (dir->passwordfile != NULL) { session->host->auth_method = dir->auth_method; session->host->passwordfile = dir->passwordfile; if (dir->groupfile != NULL) { session->host->groupfile = dir->groupfile; } } if (dir->required_group.size > 0) { session->host->required_group.size = dir->required_group.size; session->host->required_group.item = dir->required_group.item; } if (dir->alter_group.size > 0) { session->host->alter_group.size = dir->alter_group.size; session->host->alter_group.item = dir->alter_group.item; } break; } else { return 500; } } } dir = dir->next; } return 200; } /* Complete path in chrooted session? * * */ char *file_in_chroot(t_session *session, char *file) { size_t length; if (session->config->server_root != NULL) { length = strlen(session->config->server_root); if (strlen(session->host->passwordfile) > length) { if (memcmp(session->host->passwordfile, session->config->server_root, length) == 0) { file += length; } } } return file; } /* Check if User-Agent string contains deny_bot substring. */ bool client_is_rejected_bot(t_session *session) { int i, len; char *useragent; t_denybotlist *botlist; if (session->host->deny_bot == NULL) { return false; } else if ((useragent = get_headerfield("User-Agent:", session->headerfields)) == NULL) { return false; } botlist = session->host->deny_bot; while (botlist != NULL) { if (strstr(useragent, botlist->bot) != NULL) { for (i = 0; i < botlist->uri.size; i++) { len = strlen(*(botlist->uri.item + i)); if (session->uri_len >= len) { if (memcmp(*(botlist->uri.item + i), session->uri, len) == 0) { return true; } } } } botlist = botlist->next; } return false; } void close_socket(t_session *session) { if (session->socket_open) { #ifdef HAVE_SSL if (session->binding->use_ssl) { ssl_close(session->ssl_data); } #endif fsync(session->client_socket); close(session->client_socket); session->socket_open = false; } } #ifdef DEBUG void set_client_status(t_session *session, char *status) { sfree(session->status); if (status != NULL) { session->status = strdup(status); } else { session->status = NULL; } } #endif