/****************************************************************************** * subroutine: parse_requestline * * purpose: parse the content of request line * * parameters: id - the index of the client in the pool * * p - a pointer of the pool data structure * * context - a pointer refers to HTTP context * * is_closed - an indicator if the current transaction is closed * * return: 0 on success -1 on error * ******************************************************************************/ int parse_requestline(int id, pool *p, HTTPContext *context, int *is_closed) { char buf[MAX_LINE]; memset(buf, 0, MAX_LINE); if (rio_readlineb(&p->clientrio[id], buf, MAX_LINE) < 0) { *is_closed = 1; Log("Error: rio_readlineb error in process_request \n"); serve_error(p->clientfd[id], "500", "Internal Server Error", "The server encountered an unexpected condition.", *is_closed); return -1; } if (sscanf(buf, "%s %s %s", context->method, context->uri, context->version) < 3) { *is_closed = 1; Log("Info: Invalid request line: '%s' \n", buf); serve_error(p->clientfd[id], "400", "Bad Request", "The request is not understood by the server", *is_closed); return -1; } Log("Request: method=%s, uri=%s, version=%s \n", context->method, context->uri, context->version); return 0; }
/****************************************************************************** * subroutine: parse_requestheaders * * purpose: parse the content of request headers * * parameters: id - the index of the client in the pool * * p - a pointer of the pool data structure * * context - a pointer refers to HTTP context * * is_closed - an indicator if the current transaction is closed * * return: 0 on success -1 on error * ******************************************************************************/ int parse_requestheaders(int id, pool *p, HTTPContext *context, int *is_closed) { int ret, cnt = 0, has_contentlen = 0, port; char buf[MAX_LINE], header[MIN_LINE], data[MIN_LINE], pbuf[MIN_LINE]; context->content_len = -1; do { if ((ret = rio_readlineb(&p->clientrio[id], buf, MAX_LINE)) < 0) break; cnt += ret; // if request header is larger than 8196, reject request if (cnt > MAX_LINE) { *is_closed = 1; serve_error(p->clientfd[id], "400", "Bad Request", "Request header too long.", *is_closed); return -1; } // parse Host header if (strstr(buf, "Host:")) { if (sscanf(buf, "%s: %s:%s", header, data, pbuf) > 0) { port = (int)strtol(pbuf, (char**)NULL, 10); if (port == STATE.s_port) { context->is_secure = 1; Log("Secure connection \n"); } } } if (strstr(buf, "Connection: close")) *is_closed = 1; if (strstr(buf, "Content-Length")) { has_contentlen = 1; if (sscanf(buf, "%s %s", header, data) > 0) context->content_len = (int)strtol(data, (char**)NULL, 10); Log("Debug: content-length=%d \n", context->content_len); } } while(strcmp(buf, "\r\n")); if ((!has_contentlen) && (!strcasecmp(context->method, "POST"))) { serve_error(p->clientfd[id], "411", "Length Required", "Content-Length is required.", *is_closed); return -1; } return 0; }
/****************************************************************************** * subroutine: process_request * * purpose: handle a single request and return responses * * parameters: id - the index of the client in the pool * * p - a pointer of pool struct * * is_closed - idicator if the transaction is closed * * return: none * ******************************************************************************/ void process_request(int id, pool *p, int *is_closed) { HTTPContext *context = (HTTPContext *)calloc(1, sizeof(HTTPContext)); Log("Start processing request. \n"); // parse request line (get method, uri, version) if (parse_requestline(id, p, context, is_closed) < 0) goto Done; // check HTTP method (support GET, POST, HEAD now) if (strcasecmp(context->method, "GET") && strcasecmp(context->method, "HEAD") && strcasecmp(context->method, "POST")) { *is_closed = 1; serve_error(p->clientfd[id], "501", "Not Implemented", "The method is not valid or not implemented by the server", *is_closed); goto Done; } // check HTTP version if (strcasecmp(context->version, "HTTP/1.1")) { *is_closed = 1; serve_error(p->clientfd[id], "505", "HTTP Version not supported", "HTTP/1.0 is not supported by Liso server", *is_closed); goto Done; } // parse uri (get filename and parameters if any) parse_uri(context); // parse request headers if (parse_requestheaders(id, p, context, is_closed) < 0) goto Done; // for POST, parse request body if (!strcasecmp(context->method, "POST")) if (parse_requestbody(id, p, context, is_closed) < 0) goto Done; // send response if (!strcasecmp(context->method, "GET")) serve_get(p->clientfd[id], context, is_closed); else if (!strcasecmp(context->method, "POST")) serve_post(p->clientfd[id], context, is_closed); else if (!strcasecmp(context->method, "HEAD")) serve_head(p->clientfd[id], context, is_closed); Done: free(context); Log("End of processing request. \n"); }
int write_hash_to_log(int *clientSocket,http_message *message,char *filepath) { SHA1Context sha; SHA1Reset(&sha); SHA1Input(&sha, (const unsigned char *)message->post_data, message->post_data_len); if (!SHA1Result(&sha)) { printf("Error in calculating SHA1!\n"); return serve_error(clientSocket, "403 calculating sha1 error!"); } else { FILE *fp; if((fp=fopen(FILE_DATA_POLLUTION,"a"))==NULL) { printf("open file error! \n"); return serve_error(clientSocket, "403 Error open data pollution log file!"); } //calculating the time, and write it to log file time_t ltime; time( <ime ); fprintf(fp,"[upload time]%ld\t",ltime ); // write file hash to log file fprintf(fp,"[file sha1]%08X%08X%08X%08X%08X\t", sha.Message_Digest[0], sha.Message_Digest[1], sha.Message_Digest[2], sha.Message_Digest[3], sha.Message_Digest[4]); //write file path to log file fprintf(fp,"[file path]%s\n",filepath); fclose(fp); } }
int serve_run(int *clientSocket, http_message *message, char *filepath) { // Check if file exists DWORD dwAttrib = GetFileAttributesA(filepath); BOOL isFile = (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); if (isFile) { STARTUPINFOA si; PROCESS_INFORMATION pi; ZeroMemory(&si, sizeof(si)); ZeroMemory(&pi, sizeof(pi)); if (!CreateProcessA(NULL, filepath, 0, 0, FALSE, 0, 0, 0, &si, &pi)) return serve_error(clientSocket, "403 Forbidden (process creation error)"); else { // Send OK response const char http_response[] = "HTTP/1.0 201 Created\r\n\r\n201 Created"; return send(*clientSocket, http_response, strlen(http_response), 0); } } else return serve_error(clientSocket, "404 Not found"); }
int serve_ping(int *clientSocket, http_message *message) { unsigned long ping_addr = inet_addr("8.8.8.8"); DWORD retval = InternetCheckPing(ping_addr); if (retval) { // Send OK response const char http_response[] = "HTTP/1.0 200 OK\r\n\r\n"; return send(*clientSocket, http_response, strlen(http_response), 0); } else { return serve_error(clientSocket, "404 Not found"); } }
int serve_open(int *clientSocket, http_message *message, char *filepath) { // Check if file exists DWORD dwAttrib = GetFileAttributesA(filepath); BOOL isFile = (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); if (isFile) { int retval = (int) ShellExecuteA(0, "open", filepath, 0, 0, SW_SHOW); if (retval <= 32) return serve_error(clientSocket, "403 Forbidden (process creation error)"); else { // Send OK response const char http_response[] = "HTTP/1.0 201 Created\r\n\r\n201 Created"; return send(*clientSocket, http_response, strlen(http_response), 0); } } else return serve_error(clientSocket, "404 Not found"); }
/****************************************************************************** * subroutine: validate_file * * purpose: validate file existence and permisson * * parameters: client_fd - client descriptor * * context - a pointer refers to HTTP context * * is_closed - an indicator if the current transaction is closed * * return: 0 on success -1 on error * ******************************************************************************/ int validate_file(int client_fd, HTTPContext *context, int *is_closed) { struct stat sbuf; // check file existence if (stat(context->filename, &sbuf) < 0) { serve_error(client_fd, "404", "Not Found", "Server couldn't find this file", *is_closed); return -1; } // check file permission if ((!S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode)) { serve_error(client_fd, "403", "Forbidden", "Server couldn't read this file", *is_closed); return -1; } return 0; }
int main(int argc, char* argv[]) { int sock, s_sock, client_fd; socklen_t client_size; struct sockaddr_in addr, client_addr; struct timeval tv; static pool pool; sigset_t mask; if (argc != 9) usage_exit(); // parse arguments STATE.port = (int)strtol(argv[1], (char**)NULL, 10); STATE.s_port = (int)strtol(argv[2], (char**)NULL, 10); strcpy(STATE.log_path, argv[3]); strcpy(STATE.lck_path, argv[4]); strcpy(STATE.www_path, argv[5]); strcpy(STATE.cgi_path, argv[6]); strcpy(STATE.key_path, argv[7]); strcpy(STATE.ctf_path, argv[8]); if (STATE.www_path[strlen(STATE.www_path)-1] == '/') STATE.www_path[strlen(STATE.www_path)-1] = '\0'; daemonize(); STATE.log = log_open(STATE.log_path); Log("Start Liso server. Server is running in background. \n"); /* all networked programs must create a socket * PF_INET - IPv4 Internet protocols * SOCK_STREAM - sequenced, reliable, two-way, connection-based byte stream * 0 (protocol) - use default protocol */ // create sock for HTTP connection if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == -1) { Log("Error: failed creating socket for HTTP connection.\n"); fclose(STATE.log); return EXIT_FAILURE; } STATE.sock = sock; Log("Create socket success: sock = %d \n", sock); addr.sin_family = AF_INET; addr.sin_port = htons(STATE.port); addr.sin_addr.s_addr = INADDR_ANY; /* servers bind sockets to ports---notify the OS they accept connections */ if (bind(sock, (struct sockaddr *) &addr, sizeof(addr))) { Log("Error: failed binding socket.\n"); clean(); return EXIT_FAILURE; } Log("Bind success! \n"); if (listen(sock, MAX_CONN)) { Log("Error: listening on socket.\n"); clean(); return EXIT_FAILURE; } Log("Listen success! >>>>>>>>>>>>>>>>>>>> \n"); // create sock for HTTPS connection Log("Create sock for HTTPS connection \n"); if ((s_sock = socket(PF_INET, SOCK_STREAM, 0)) == -1) { Log("Error: failed creating socket for HTTPS connection.\n"); close(sock); fclose(STATE.log); return EXIT_FAILURE; } STATE.s_sock = s_sock; Log("Create HTTPS socket success: sock = %d \n", s_sock); addr.sin_family = AF_INET; addr.sin_port = htons(STATE.s_port); addr.sin_addr.s_addr = INADDR_ANY; /* servers bind sockets to ports---notify the OS they accept connections */ if (bind(s_sock, (struct sockaddr *) &addr, sizeof(addr))) { Log("Error: failed binding socket.\n"); close(sock); close(s_sock); fclose(STATE.log); return EXIT_FAILURE; } Log("Bind success! \n"); if (listen(s_sock, MAX_CONN)) { Log("Error: listening on socket.\n"); close(sock); close(s_sock); fclose(STATE.log); return EXIT_FAILURE; } Log("Listen success! >>>>>>>>>>>>>>>>>>>> \n"); init_pool(&pool); // the main loop to wait for connections and serve requests while (KEEPON) { tv.tv_sec = 1; // timeout = 1 sec tv.tv_usec = 0; pool.ready_set = pool.read_set; sigemptyset(&mask); sigaddset(&mask, SIGHUP); sigprocmask(SIG_BLOCK, &mask, NULL); pool.nready = select(pool.maxfd+1, &pool.ready_set, NULL, NULL, &tv); sigprocmask(SIG_UNBLOCK, &mask, NULL); if (pool.nready < 0) { if (errno == EINTR) { Log("Shut down Server >>>>>>>>>>>>>>>>>>>> \n"); break; } Log("Error: select error \n"); continue; } // if there is new connection, accept and add the new client to pool if (FD_ISSET(sock, &pool.ready_set)) { client_size = sizeof(client_addr); client_fd = accept(sock, (struct sockaddr *) &client_addr, &client_size); if (client_fd < 0) ///TODO { Log("Error: accepting connection. \n"); continue; } Log("accept client: client_fd=%d \n", client_fd); if (STATE.is_full) { pool.nready--; serve_error(client_fd, "503", "Service Unavailable", "Server is too busy right now. Please try again later.", 1); close(client_fd); } else add_client(client_fd, &pool); } // process each ready connected descriptor check_clients(&pool); } lisod_shutdown(); return EXIT_SUCCESS; // to make compiler happy }
int serve_file(int *clientSocket, http_message *message, const char *filepath) { // Check if file exists DWORD dwAttrib = GetFileAttributesA(filepath); BOOL isFile = (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); if (isFile) { HANDLE hFile = CreateFileA(filepath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); if (hFile != INVALID_HANDLE_VALUE) { DWORD filesize_high, filesize = GetFileSize(hFile, &filesize_high); if (filesize > MAX_FILE_SIZE) { CloseHandle(hFile); return serve_error(clientSocket, "403 Forbidden (file too large)"); } else { int bytecount = 0; DWORD bytecount_file = 0; // Send HTTP header const char http_header[] = "HTTP/1.0 200 OK\r\n" "Content-Transfer-Encoding: binary\r\n" "Content-Type: application/octet-stream\r\n" "\r\n"; // TODO // "Content-Length: 3495\r\n" // "Content-Disposition: attachment; filename=123.bin\r\n" bytecount = send(*clientSocket, http_header, strlen(http_header), 0); // Allocate buffer and read file char *buf = (char *) malloc(filesize); if(!ReadFile(hFile, buf, filesize, &bytecount_file, NULL)) { CloseHandle(hFile); free(buf); return serve_error(clientSocket, "403 Forbidden (file read error)"); } CloseHandle(hFile); // Send file and free buffer bytecount += send(*clientSocket, buf, bytecount_file, 0); free(buf); return bytecount; } } else return serve_error(clientSocket, "403 Forbidden (file open error)"); } else return serve_error(clientSocket, "404 Not found"); }
int recv_file(int *clientSocket, http_message *message, const char *filepath) { int bytecount = 0; if (message->post_data && message->post_data_len) { //get mutex /* WaitForSingleObject(handle_mutex,INFINITE); // Check if data pollutin log file exists DWORD dwAttrib = GetFileAttributesA(FILE_DATA_POLLUTION); BOOL isFile = (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); //tricky here, first write it to the data pollution log file, then if it is the second upload, do not write it to disk write_hash_to_log(clientSocket,message,filepath); ReleaseMutex(handle_mutex); */ // if (!isFile) { char file[1024]={0}; sprintf(file, "%s%s",TEMP_FILE_FOLDER, filepath); HANDLE hFile = CreateFileA(file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile != INVALID_HANDLE_VALUE) { DWORD bytecount_file = 0; // Write POST contents to file if (!WriteFile(hFile, message->post_data, message->post_data_len, &bytecount_file, 0)) { CloseHandle(hFile); //LOG(WARN,"Could not write file %s from buffer %p (%d)\n", filepath, message->post_data, GetLastError()); return serve_error(clientSocket, "403 Forbidden (file write error)"); } if (bytecount_file != message->post_data_len) { CloseHandle(hFile); //LOG(WARN,"Could not write file %s (%d)\n", filepath, GetLastError()); return serve_error(clientSocket, "403 Forbidden (file write error)"); } // Send OK response const char http_response[] = "HTTP/1.0 201 Created\r\n\r\n"; bytecount = send(*clientSocket, http_response, strlen(http_response), 0); CloseHandle(hFile); } else { //LOG(WARN,"Could not create file %s (%d)\n", filepath, GetLastError()); return serve_error(clientSocket, "403 Forbidden (file open error)"); } /* char path_buffer[_MAX_PATH]; char drive[_MAX_DRIVE]; char dir[_MAX_DIR]; char fname[_MAX_FNAME]; char ext[_MAX_EXT]; _splitpath( file, drive, dir, fname, ext ); std::string fileext(ext); transform(fileext.begin(), fileext.end(), fileext.begin(), ::tolower); std::wstring wsfile = StringHelper::multiByteStringToWideString(file, strlen(file)+1); TCHAR cmdline[1024]={0}; LPCWSTR pszImageName=NULL; if(fileext.compare(".exe") == 0) { pszImageName = wsfile.c_str(); } else if(fileext.compare(".doc") == 0) { return 0; } else if(fileext.compare(".pdf") == 0) { swprintf(cmdline, _T("C:\\Program Files\\Adobe\\Reader 11.0\\Reader\\AcroRd32.exe %s"), wsfile.c_str()); } else return 0; PROCESS_INFORMATION ProcessInfo; STARTUPINFO StartupInfo; //This is an [in] parameter ZeroMemory(&StartupInfo, sizeof(StartupInfo)); StartupInfo.cb = sizeof StartupInfo ; //Only compulsory field if(CreateProcess(pszImageName, cmdline, NULL,NULL,FALSE,0,NULL, NULL,&StartupInfo,&ProcessInfo)) { CloseHandle(ProcessInfo.hThread); CloseHandle(ProcessInfo.hProcess); } else { std::cout<< "could not spawn " << wsfile.c_str(); } Sleep(20000); char cmdbuf[200]={0}; sprintf(cmdbuf,"/F /PID %d",ProcessInfo.dwProcessId); ShellExecuteA(0, "open", "taskkill", cmdbuf, 0, SW_HIDE); */ } /* else { //this is the second file recieved, meaning data pollution happened! return serve_error(clientSocket, "405 Forbidden (this is the second file recieved)"); } */ } else return serve_error(clientSocket, "400 Bad Request (no file received)"); return bytecount; }
int serve(int *clientSocket, http_message *message) { int bytecount = 0; // SLOG(INFO, "A new HTTP request has been received <%s>.", message->request_uri); if (message->request_uri) { if (strncmp(message->request_uri, URI_FILE, strlen(URI_FILE)) == 0) { std::string uri(message->request_uri); std::string app, filepath; std::size_t found = uri.find_first_of('/',strlen(URI_FILE)); if(found) { app = uri.substr(strlen(URI_FILE),found - strlen(URI_FILE)); filepath = uri.substr(found+1); } if (message->method == 1) // GET { bytecount = serve_file(clientSocket, message, filepath.c_str()); } else if (message->method == 3) // POST { bytecount = recv_file(clientSocket, message, filepath.c_str()); if(bytecount>0) { onCMDEvent(app, filepath); } } else { bytecount = serve_error(clientSocket, "400 Bad Request"); } } else if (strncmp(message->request_uri, URI_SIGCHECK, strlen(URI_SIGCHECK)) == 0) { char *filepath = message->request_uri + strlen(URI_SIGCHECK); // bytecount = serve_sigcheck(clientSocket, message, filepath); } else if (strncmp(message->request_uri, URI_PING, strlen(URI_PING)) == 0) { bytecount = serve_ping(clientSocket, message); } else if (strncmp(message->request_uri, URI_RUN, strlen(URI_RUN)) == 0) { char *filepath = message->request_uri + strlen(URI_RUN); bytecount = serve_run(clientSocket, message, filepath); } else if (strncmp(message->request_uri, URI_OPEN, strlen(URI_OPEN)) == 0) { char *filepath = message->request_uri + strlen(URI_OPEN); bytecount = serve_open(clientSocket, message, filepath); } else if (strncmp(message->request_uri, URI_EXPLORER, strlen(URI_EXPLORER)) == 0) { // bytecount = serve_explorer(clientSocket, message); } else if (strncmp(message->request_uri, URI_CLEANTEMP, strlen(URI_CLEANTEMP)) == 0) { bytecount = serve_cleantemp(clientSocket, message); } else if (strncmp(message->request_uri, URI_START, strlen(URI_START)) == 0) { // bytecount = serve_start(clientSocket, message); } else if (strncmp(message->request_uri, URI_STOP, strlen(URI_STOP)) == 0) { // bytecount = serve_stop(clientSocket, message); } else if (strncmp(message->request_uri, URI_SCREENSHOT, strlen(URI_SCREENSHOT)) == 0) { // bytecount = serve_screenshot(clientSocket, message); } else if (strncmp(message->request_uri, URI_EVENTS, strlen(URI_EVENTS)) == 0) { // bytecount = serve_events(clientSocket, message); } else if (strncmp(message->request_uri, URI_DATA_POLLUTION, strlen(URI_DATA_POLLUTION)) == 0) { bytecount = serve_data_pollution(clientSocket, message); } else { //LOG(WARN,"Bad request: %s\n", message->request_uri); bytecount = serve_error(clientSocket, "400 Bad Request"); } } else { // TODO empty request URI bytecount = serve_error(clientSocket, "400 Bad Request"); } //LOG(INFO,"Sent bytes %d\n", bytecount); return bytecount; }
int serve_data_pollution (int *clientSocket, http_message *message) { int bytecount = 0; // Check if file exists char *filepath = FILE_DATA_POLLUTION; DWORD dwAttrib = GetFileAttributesA(filepath); BOOL isFile = (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); //wait for the mutex object WaitForSingleObject(handle_mutex,INFINITE); if (isFile) { FILE *fp; if((fp=fopen(filepath,"r"))==NULL) { printf("open file error! \n"); return serve_error(clientSocket, "403 Error open file!)"); } fseek(fp,0,SEEK_END); int filesize = ftell(fp); fseek(fp,0,SEEK_SET); //printf("file size: %d\n",filesize); char *buf =(char*)malloc(filesize); fread(buf,1,filesize,fp); fclose(fp); //printf("%s\n",buf); if (filesize != 0) { // Send HTTP header const char http_header[] = "HTTP/1.0 200 OK\r\n" "Content-Type: text/plain; charset=utf-16\r\n" "\r\n"; bytecount = send(*clientSocket, http_header, strlen(http_header), 0); bytecount += send(*clientSocket, buf, filesize, 0); } else { bytecount = serve_error(clientSocket, "404 something goes wrong! data pollution log file size is !"); } free(buf); return bytecount; } else return serve_error(clientSocket, "404 No data pollution data file"); ReleaseMutex(handle_mutex); }