diff --git a/Makefile b/Makefile index ab05a76..26b05dd 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -LIBRARIES = -Iinclude +LIBRARIES = `pkg-config --libs openssl` -Iinclude SOURCES = ./src/* ./cmd/server.c OUTPUT_DIR = ./bin OUTPUT = -o ${OUTPUT_DIR}/PROG @@ -18,11 +18,20 @@ output_dir: CERT_DIR = ./certs/ -genCerts: - mkdir -p ${CERT_DIR} - openssl req -new -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \ - -x509 -nodes -days 365 -out ${CERT_DIR}cert.pem -keyout ${CERT_DIR}cert.pem \ - -subj "/C=US/ST=Some-State/L=[]/O=[]/CN=localhost" - clean: rm -rf $(OUTPUT_DIR) ${CERT_DIR} **.h.gch + +genCerts: + mkdir -p ${CERT_DIR} + touch ${CERT_DIR}index.txt + openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \ + -sha256 -nodes -out ${CERT_DIR}cacert.pem -keyout ${CERT_DIR}cakey.pem \ + -outform PEM -subj "/C=US/ST=Some-State/L=[]/O=[]/CN=localhost" + openssl req -new -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \ + -nodes -days 365 -out ${CERT_DIR}cert.csr -keyout ${CERT_DIR}key.pem \ + -subj "/C=US/ST=Some-State/L=[]/O=[]/CN=localhost" + openssl ca -policy signing_policy -extensions signing_req \ + -cert ./certs/cacert.pem -keyfile ./certs/cakey.pem \ + -rand_serial -config ./ca/ca.cnf \ + -out ./certs/cert.pem -infiles ./certs/cert.csr + diff --git a/ca/ca.cnf b/ca/ca.cnf new file mode 100644 index 0000000..115e665 --- /dev/null +++ b/ca/ca.cnf @@ -0,0 +1,87 @@ +HOME = . + +#################################################################### +[ ca ] +default_ca = CA_default # The default ca section + +[ CA_default ] +base_dir = ./certs +certificate = $base_dir/cacert.pem # The CA certifcate +private_key = $base_dir/cakey.pem # The CA private key +new_certs_dir = $base_dir # Location for new certs after signing +database = $base_dir/index.txt # Database index file +serial = $base_dir/serial.txt # The current serial number + +unique_subject = no # Set to 'no' to allow creation of + # several certificates with same subject. + +default_days = 1000 # How long to certify for +default_crl_days = 30 # How long before next CRL +default_md = sha256 # Use public key default MD +preserve = no # Keep passed DN ordering + +x509_extensions = ca_extensions # The extensions to add to the cert + +email_in_dn = no # Don't concat the email in the DN +copy_extensions = copy # Required to copy SANs from CSR to cert + +#################################################################### +[ req ] +default_bits = 4096 +default_keyfile = cakey.pem +distinguished_name = ca_distinguished_name +x509_extensions = ca_extensions +string_mask = utf8only + +#################################################################### +[ ca_distinguished_name ] +countryName = Country Name (2 letter code) +countryName_default = US + +stateOrProvinceName = State or Province Name (full name) +stateOrProvinceName_default = Maryland + +localityName = Locality Name (eg, city) +localityName_default = Baltimore + +organizationName = Organization Name (eg, company) +organizationName_default = Test CA, Limited + +organizationalUnitName = Organizational Unit (eg, division) +organizationalUnitName_default = Server Research Department + +commonName = Common Name (e.g. server FQDN or YOUR name) +commonName_default = Test CA + +emailAddress = Email Address +emailAddress_default = test@example.com + +#################################################################### +[ ca_extensions ] + +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always, issuer +basicConstraints = critical, CA:true +keyUsage = keyCertSign, cRLSign +#################################################################### +[ alternate_names ] + +DNS.1 = localhost +IP.1 = 127.0.0.1 + +#################################################################### +[ signing_policy ] +countryName = optional +stateOrProvinceName = optional +localityName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +#################################################################### +[ signing_req ] +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer +basicConstraints = CA:FALSE +keyUsage = digitalSignature, keyEncipherment diff --git a/cmd/server.c b/cmd/server.c index 321cd84..1f11c86 100644 --- a/cmd/server.c +++ b/cmd/server.c @@ -2,18 +2,29 @@ #include #include #include +#include #include #include +#include // Local Includes #include "httpStruct.h" #include "socketHelp.h" #include "returnRequest.h" +#include "server.h" -#define PORT 8080 +static int verbose_flag = 0; +bool enableHTTPS = 0; + +int printDebug(char message[]) { + if (verbose_flag == 1) { + printf("[Debug] %s\n", message); + } + return 0; +} int parseHTTPRequest(char buffer[], struct HTTPRequest *r) { - char temp[1]; + char temp[1]; // Used to check newlines char *token = calloc(8, sizeof(char)); int line = 0; char *checkLine = calloc(1000, sizeof(char));; @@ -63,29 +74,112 @@ int parseHTTPRequest(char buffer[], struct HTTPRequest *r) { return 0; } -int handleRequest(char buffer[], int socket) { +int handleRequest(char buffer[], int socket, SSL *ssl) { struct HTTPRequest r; // Holds relevant HTTP request information int checkerr = 0; + + printDebug("Received Request"); + // Grabbing relevant information out of request checkerr = parseHTTPRequest(buffer, &r); if (checkerr != 0) { // Checking for HTTP parsing error printf("Error parsing: exit code %d\n", checkerr); - return return404Request(socket); + return return404Request(socket, ssl); } // Return response to socket - return200Request(socket); + return200Request(socket, ssl); return 0; } -int main(int argc, char const *argv[]) { +int main(int argc, char **argv) { struct sockaddr_in address; int server_fd, new_socket; - int checkerr = 0; + int port = 0; // Define what port server listens on + int checkerr = 0; // Used for error checking int addrlen = sizeof(address); + char *certFile = malloc(0); + char *privKeyFile = malloc(0); + + SSL_CTX *ctx = NULL; + char buffer[1024] = {}; - checkerr = createSocket(PORT, &server_fd, &address, &addrlen); + // Setting up options + static const struct option long_options[] = { + {"help", no_argument, NULL, 'h'}, + {"port", required_argument, NULL, 'p'}, + {"cert", required_argument, NULL, 'c'}, + {"privkey", required_argument, NULL, 'k'}, + {"verbose", no_argument, &verbose_flag, 1}, + {0, 0, 0, 0} + }; + + const char* usage = + "Usage: seaweb [options]\n\n" + " -h --help\t\t\tShows this message\n" + " -p --port\t\t\tStarts webserver on passed port\n" + " -c --cert\t\t\tPath to certificate\n" + " -k --privkey\t\t\tPath to private key\n" + "\n" + "\n" + " --verbose\t\t\tPrints debug messages\n" + "\n"; + + int c; + while (1) { + int option_index = 0; + c = getopt_long(argc, argv, "hp:c:k:", long_options, &option_index); + if(c == -1) { // Break if no more options are present to parse + break; + } + switch(c) { + case 'h': + printf("%s", usage); + exit(EXIT_SUCCESS); + case 'p': + sscanf(optarg, "%d", &port); + break; + case 'c': + certFile = calloc(strlen(optarg), sizeof(char)); + strcpy(certFile, optarg); + printf("Cert: %s\n", certFile); + break; + case 'k': + privKeyFile = calloc(strlen(optarg), sizeof(char)); + strcpy(privKeyFile, optarg); + printf("Priv: %s\n", privKeyFile); + break; + } + } + + // Argument checks + if (port == 0) { // Setting default port if none is passed + port = 8080; + } + if (!((strlen(certFile) > 0) == (strlen(privKeyFile) > 0))) { // XNOR of string lengths + // Checking what flag was not passed + if (strlen(certFile)) { // Privkey not passed + printf("If certificate file is specified, a private key files needs to be specified\n"); + } else if (strlen(privKeyFile)) { // Cert not passed + printf("If privkey file is specified, a certificate files needs to be specified\n"); + } + printf("Exiting...\n"); + exit(EXIT_FAILURE); + } else if ( (strlen(certFile) > 0) && (strlen(privKeyFile) > 0) ) { // Enabling HTTPS + enableHTTPS = 1; + } + + if ( enableHTTPS == 1 ) { + printf("Opening secure socket on port: %d\n", port); + checkerr = createSecureSocket(port, &server_fd, &address, &addrlen, &ctx, certFile, privKeyFile); + if ( ctx == NULL ) { + printf("Error creating ctx\n"); + } + } else { + printf("Opening socket on port: %d\n", port); + checkerr = createSocket(port, &server_fd, &address, &addrlen); + } if (checkerr != 0) { perror("Error creating socket"); @@ -99,10 +193,23 @@ int main(int argc, char const *argv[]) { perror("Accept connection error"); exit(EXIT_FAILURE); } - read(new_socket, buffer, 1024); - handleRequest(buffer, new_socket); + if ( enableHTTPS ) { + SSL *ssl; + ssl = SSL_new(ctx); + SSL_set_fd(ssl, new_socket); + SSL_accept(ssl); + SSL_read(ssl, buffer, sizeof(buffer)); + handleRequest(buffer, new_socket, ssl); + } else { + read(new_socket, buffer, 1024); + handleRequest(buffer, new_socket, NULL); + } + close(new_socket); } + free(privKeyFile); + free(certFile); + close(server_fd); exit(EXIT_SUCCESS); } diff --git a/include/returnRequest.h b/include/returnRequest.h index 5349aa2..6eda11e 100644 --- a/include/returnRequest.h +++ b/include/returnRequest.h @@ -1,3 +1,5 @@ +#include + int returnRequest(int socket, char *message, int status); -int return200Request(int socket); -int return404Request(int socket); +int return200Request(int socket, SSL *ssl); +int return404Request(int socket, SSL *ssl); diff --git a/include/server.h b/include/server.h new file mode 100644 index 0000000..d871840 --- /dev/null +++ b/include/server.h @@ -0,0 +1,4 @@ +#include + +extern bool enableHTTPS; + diff --git a/include/socketHelp.h b/include/socketHelp.h index 6355b8e..e0c770f 100644 --- a/include/socketHelp.h +++ b/include/socketHelp.h @@ -1,5 +1,7 @@ #include #include +#include int createSocket(int port, int *server_fd, struct sockaddr_in *address, int *addrlen); +int createSecureSocket(int port, int *server_fd, struct sockaddr_in *address, int *addrlen, SSL_CTX **ctx, char certFile[], char keyFile[]); diff --git a/src/returnRequest.c b/src/returnRequest.c index d1f6772..3fef9e8 100644 --- a/src/returnRequest.c +++ b/src/returnRequest.c @@ -1,18 +1,29 @@ +#include +#include #include #include +#include -int returnRequest(int socket, char *message, int status) { - send(socket, message, strlen(message), 0); +#include "server.h" + +int returnRequest(int socket, char *message, int status, SSL *ssl) { + if ( enableHTTPS ) { + SSL_write(ssl, message, strlen(message)); + SSL_get_fd(ssl); + SSL_free(ssl); + } else { + send(socket, message, strlen(message), 0); + } return 0; } -int return200Request(int socket) { +int return200Request(int socket, SSL *ssl) { char *message = "HTTP/1.1 200 OK\nContent-Length: 6\nConnection: close\n\nhello\n"; - return returnRequest(socket, message, 200); + return returnRequest(socket, message, 200, ssl); } -int return404Request(int socket) { +int return404Request(int socket, SSL *ssl) { char *message = "HTTP/1.1 404 Not Found\nContent-Length: 12\nConnection: close\n\n404 Request\n"; - return returnRequest(socket, message, 400); + return returnRequest(socket, message, 400, ssl); } diff --git a/src/socketHelp.c b/src/socketHelp.c index 3519ba3..4b17e74 100644 --- a/src/socketHelp.c +++ b/src/socketHelp.c @@ -3,6 +3,59 @@ #include #include +#include +#include + +#include "socketHelp.h" + +SSL_CTX* InitServerCTX() { + SSL_METHOD *method; + SSL_CTX *ctx; + + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + method = TLS_server_method(); + ctx = SSL_CTX_new(method); + if ( ctx == NULL ) { + ERR_print_errors_fp(stderr); + exit(EXIT_FAILURE); + } + return ctx; +} + +void LoadCertificates(SSL_CTX* ctx, char* certFile, char* keyFile) { + // Set local certificate from certFile + if ( SSL_CTX_use_certificate_file(ctx, certFile, SSL_FILETYPE_PEM) <= 0 ) { + ERR_print_errors_fp(stderr); + exit(EXIT_FAILURE); + } + // Set local priv key from keyFile + if ( SSL_CTX_use_PrivateKey_file(ctx, keyFile, SSL_FILETYPE_PEM) <=0 ) { + ERR_print_errors_fp(stderr); + exit(EXIT_FAILURE); + } + // Verify priv key + if ( !SSL_CTX_check_private_key(ctx) ) { + fprintf(stderr, "Private key does not match passed certificate file\n"); + exit(EXIT_FAILURE); + } + return; +} + +int createSecureSocket(int port, int *server_fd, struct sockaddr_in *address, int *addrlen, SSL_CTX **ctx, char certFile[], char keyFile[]) { + + SSL_library_init(); + *ctx = InitServerCTX(); + LoadCertificates(*ctx, certFile, keyFile); + + if ( createSocket(port, server_fd, address, addrlen) ) { + fprintf(stderr, "Error create socket\n"); + exit(EXIT_FAILURE); + } + + return 0; +} + int createSocket(int port, int *server_fd, struct sockaddr_in *address, int *addrlen) { int opt = 1;