diff --git a/Makefile b/Makefile index e726690..2d00feb 100644 --- a/Makefile +++ b/Makefile @@ -8,11 +8,11 @@ BIN_NAME = cfloor OBJS = obj/networking.o obj/linked.o obj/logging.o obj/signals.o obj/headers.o obj/misc.o obj/status.o obj/files.o obj/mime.o obj/cgi.o obj/util.o obj/ssl.o obj/config.o DEPS = $(OBJS:%.o=%.d) -all: $(BIN_NAME) +all: $(BIN_NAME) test ssl: CFLAGS += -DSSL_SUPPORT -Icrypto ssl: LDFLAGS += -lcrypto -lssl -ssl: obj/ssl.o $(BIN_NAME) +ssl: obj/ssl.o $(BIN_NAME) test $(BIN_NAME): obj/main.o $(OBJS) $(LD) $(LDFLAGS) -o $@ $^ diff --git a/src/config.c b/src/config.c index c1f0ab7..2c2a6a9 100644 --- a/src/config.c +++ b/src/config.c @@ -9,6 +9,10 @@ #include "util.h" #include "networking.h" +#ifdef SSL_SUPPORT +#include "ssl.h" +#endif + #define LENGTH_OF_TOFREE_ARRAY (256) #define MAX_TOKEN_LENGTH (128) @@ -44,22 +48,28 @@ struct config* config_parse(FILE* file) { #define BIND (0) - #define BIND_VALUE (1) - #define BIND_BRACKETS_OPEN (2) - #define BIND_CONTENT (4) - #define SITE_BRACKETS_OPEN (5) - #define SITE_CONTENT (7) - #define SITE_HOST_EQUALS (8) - #define SITE_HOST_VALUE (9) - #define SITE_ROOT_EQUALS (10) - #define SITE_ROOT_VALUE (11) - #define HANDLER_VALUE (12) - #define HANDLER_BRACKETS_OPEN (13) - #define HANDLER_CONTENT (15) - #define TYPE_EQUALS (16) - #define TYPE_VALUE (17) - #define INDEX_EQUALS (18) - #define INDEX_VALUE (19) + #define BIND_VALUE (10) + #define BIND_BRACKETS_OPEN (11) + #define BIND_CONTENT (12) + #define SSL_BRACKETS_OPEN (130) + #define SSL_CONTENT (131) + #define SSL_KEY_EQUALS (132) + #define SSL_KEY_VALUE (133) + #define SSL_CERT_EQUALS (134) + #define SSL_CERT_VALUE (135) + #define SITE_BRACKETS_OPEN (140) + #define SITE_CONTENT (141) + #define SITE_HOST_EQUALS (142) + #define SITE_HOST_VALUE (143) + #define SITE_ROOT_EQUALS (144) + #define SITE_ROOT_VALUE (145) + #define HANDLER_VALUE (1460) + #define HANDLER_BRACKETS_OPEN (1461) + #define HANDLER_CONTENT (1462) + #define TYPE_EQUALS (1463) + #define TYPE_VALUE (1464) + #define INDEX_EQUALS (1465) + #define INDEX_VALUE (1466) int state = BIND; struct config_bind* currentBind = NULL; @@ -83,6 +93,9 @@ struct config* config_parse(FILE* file) { currentToken[currentTokenLength] = '\0'; + char* tmp; + char** tmpArray; + switch(state) { case BIND: if (strcmp(currentToken, "bind") != 0) { @@ -100,15 +113,15 @@ struct config* config_parse(FILE* file) { replaceOrAdd(toFree, &toFreeLength, NULL, currentBind); config->nrBinds++; - struct config_bind** tmp = realloc(config->binds, config->nrBinds * sizeof(struct config_bind*)); - if (tmp == NULL) { + struct config_bind** tmpBind = realloc(config->binds, config->nrBinds * sizeof(struct config_bind*)); + if (tmpBind == NULL) { error("config: couldn't reallocate for bind array: %s", strerror(errno)); freeEverything(toFree, toFreeLength); return NULL; } - replaceOrAdd(toFree, &toFreeLength, config->binds, tmp); - config->binds = tmp; + replaceOrAdd(toFree, &toFreeLength, config->binds, tmpBind); + config->binds = tmpBind; config->binds[config->nrBinds - 1] = currentBind; currentBind->nrSites = 0; @@ -116,6 +129,10 @@ struct config* config_parse(FILE* file) { currentBind->addr = NULL; currentBind->port = NULL; + #ifdef SSL_SUPPORT + currentBind->ssl = NULL; + #endif + state = BIND_VALUE; break; @@ -196,12 +213,103 @@ struct config* config_parse(FILE* file) { state = SITE_BRACKETS_OPEN; } else if (strcmp(currentToken, "}") == 0) { state = BIND; + } else if (strcmp(currentToken, "ssl") == 0) { + #ifdef SSL_SUPPORT + if (currentBind->ssl != NULL) { + error("config: ssl settings for this bind already defined; line %d", currentLine); + freeEverything(toFree, toFreeLength); + return NULL; + } + + currentBind->ssl = malloc(sizeof(struct ssl_settings)); + if (currentBind->ssl == NULL) { + error("config: couldn't allocate for ssl settings: %s", strerror(errno)); + freeEverything(toFree, toFreeLength); + return NULL; + } + replaceOrAdd(toFree, &toFreeLength, NULL, currentBind->ssl); + + currentBind->ssl->privateKey = NULL; + currentBind->ssl->certificate = NULL; + + state = SSL_BRACKETS_OPEN; + #else + error("config: not compiled with ssl support"); + freeEverything(toFree, toFreeLength); + return NULL; + #endif } else { error("config: Unknown property '%s' on line %d.", currentToken, currentLine); freeEverything(toFree, toFreeLength); return NULL; } break; + #ifdef SSL_SUPPORT + case SSL_BRACKETS_OPEN: + if (strcmp(currentToken, "{") != 0) { + error("config: Unexpected token '%s' on line %d. '{' expected", currentToken, currentLine); + freeEverything(toFree, toFreeLength); + return NULL; + } + + state = SSL_CONTENT; + break; + case SSL_CONTENT: + if (strcmp(currentToken, "key") == 0) { + state = SSL_KEY_EQUALS; + } else if (strcmp(currentToken, "cert") == 0) { + state = SSL_CERT_EQUALS; + } else if (strcmp(currentToken, "}") == 0) { + state = BIND_CONTENT; + } else { + error("config: Unknown property '%s' on line %d.", currentToken, currentLine); + freeEverything(toFree, toFreeLength); + return NULL; + } + break; + case SSL_KEY_EQUALS: + if (strcmp(currentToken, "=") != 0) { + error("config: Unexpected token '%s' on line %d. '=' expected", currentToken, currentLine); + freeEverything(toFree, toFreeLength); + return NULL; + } + state = SSL_KEY_VALUE; + break; + case SSL_KEY_VALUE: ; + tmp = strclone(currentToken); + if (tmp == NULL) { + error("config: error cloning ssl key string"); + freeEverything(toFree, toFreeLength); + return NULL; + } + replaceOrAdd(toFree, &toFreeLength, NULL, tmp); + + currentBind->ssl->privateKey = tmp; + + state = SSL_CONTENT; + break; + case SSL_CERT_EQUALS: + if (strcmp(currentToken, "=") != 0) { + error("config: Unexpected token '%s' on line %d. '=' expected", currentToken, currentLine); + freeEverything(toFree, toFreeLength); + return NULL; + } + state = SSL_CERT_VALUE; + break; + case SSL_CERT_VALUE: ; + tmp = strclone(currentToken); + if (tmp == NULL) { + error("config: error cloning ssl cert string"); + freeEverything(toFree, toFreeLength); + return NULL; + } + replaceOrAdd(toFree, &toFreeLength, NULL, tmp); + + currentBind->ssl->certificate = tmp; + + state = SSL_CONTENT; + break; + #endif case SITE_BRACKETS_OPEN: if (strcmp(currentToken, "{") != 0) { error("config: Unexpected token '%s' on line %d. '{' expected", currentToken, currentLine); @@ -260,9 +368,9 @@ struct config* config_parse(FILE* file) { } state = SITE_HOST_VALUE; break; - case SITE_HOST_VALUE: ; - char** tmpArray = realloc(currentSite->hostnames, ++currentSite->nrHostnames * sizeof(char*)); - if (tmp == NULL) { + case SITE_HOST_VALUE: + tmpArray = realloc(currentSite->hostnames, ++currentSite->nrHostnames * sizeof(char*)); + if (tmpArray == NULL) { error("config: error allocating hostname array"); freeEverything(toFree, toFreeLength); return NULL; @@ -270,15 +378,15 @@ struct config* config_parse(FILE* file) { replaceOrAdd(toFree, &toFreeLength, currentSite->hostnames, tmpArray); currentSite->hostnames = tmpArray; - char* clone = strclone(currentToken); - if (clone == NULL) { + tmp = strclone(currentToken); + if (tmp == NULL) { error("config: error cloning hostname string"); freeEverything(toFree, toFreeLength); return NULL; } - replaceOrAdd(toFree, &toFreeLength, NULL, clone); + replaceOrAdd(toFree, &toFreeLength, NULL, tmp); - currentSite->hostnames[currentSite->nrHostnames - 1] = clone; + currentSite->hostnames[currentSite->nrHostnames - 1] = tmp; state = SITE_CONTENT; break; @@ -291,21 +399,21 @@ struct config* config_parse(FILE* file) { state = SITE_ROOT_VALUE; break; case SITE_ROOT_VALUE: - clone = strclone(currentToken); - if (clone == NULL) { + tmp = strclone(currentToken); + if (tmp == NULL) { error("config: error cloning document root string"); freeEverything(toFree, toFreeLength); return NULL; } - replaceOrAdd(toFree, &toFreeLength, NULL, clone); + replaceOrAdd(toFree, &toFreeLength, NULL, tmp); - currentSite->documentRoot = clone; + currentSite->documentRoot = tmp; state = SITE_CONTENT; break; case HANDLER_VALUE: currentHandler->dir = strclone(currentToken); - if (clone == NULL) { + if (currentHandler->dir == NULL) { error("config: error cloning handle directory string"); freeEverything(toFree, toFreeLength); return NULL; @@ -374,7 +482,7 @@ struct config* config_parse(FILE* file) { struct fileSettings* settings = &(currentHandler->settings.fileSettings); tmpArray = realloc(settings->indexfiles.files, ++(settings->indexfiles.number) * sizeof(char*)); - if (tmp == NULL) { + if (tmpArray == NULL) { error("config: error allocating hostname array"); freeEverything(toFree, toFreeLength); return NULL; @@ -382,15 +490,15 @@ struct config* config_parse(FILE* file) { replaceOrAdd(toFree, &toFreeLength, settings->indexfiles.files, tmpArray); settings->indexfiles.files = tmpArray; - clone = strclone(currentToken); - if (clone == NULL) { + tmp = strclone(currentToken); + if (tmp == NULL) { error("config: error cloning hostname string"); freeEverything(toFree, toFreeLength); return NULL; } - replaceOrAdd(toFree, &toFreeLength, NULL, clone); + replaceOrAdd(toFree, &toFreeLength, NULL, tmp); - settings->indexfiles.files[settings->indexfiles.number - 1] = clone; + settings->indexfiles.files[settings->indexfiles.number - 1] = tmp; state = HANDLER_CONTENT; break; @@ -416,6 +524,22 @@ struct config* config_parse(FILE* file) { for (int i = 0; i < config->nrBinds; i++) { currentBind = config->binds[i]; + + #ifdef SSL_SUPPORT + if (currentBind->ssl != NULL) { + if (currentBind->ssl->privateKey == NULL) { + error("config: ssl private key missing for %s:%s", currentBind->addr, currentBind->port); + freeEverything(toFree, toFreeLength); + return NULL; + } + if (currentBind->ssl->certificate == NULL) { + error("config: ssl certificate missing for %s:%s", currentBind->addr, currentBind->port); + freeEverything(toFree, toFreeLength); + return NULL; + } + } + #endif + for(int j = 0; j < currentBind->nrSites; j++) { currentSite = currentBind->sites[j]; @@ -478,6 +602,14 @@ void config_destroy(struct config* config) { free(currentBind->addr); free(currentBind->port); + #ifdef SSL_SUPPORT + if (currentBind->ssl != NULL) { + free(currentBind->ssl->privateKey); + free(currentBind->ssl->certificate); + free(currentBind->ssl); + } + #endif + for(int j = 0; j < currentBind->nrSites; j++) { currentSite = currentBind->sites[j]; diff --git a/src/config.h b/src/config.h index caf8218..907278c 100644 --- a/src/config.h +++ b/src/config.h @@ -8,11 +8,20 @@ #include "files.h" #include "cgi.h" +#ifdef SSL_SUPPORT + #include "ssl.h" +#endif + struct config { int nrBinds; struct config_bind { char* addr; char* port; + + #ifdef SSL_SUPPORT + struct ssl_settings* ssl; + #endif + int nrSites; struct config_site { int nrHostnames; @@ -36,6 +45,10 @@ struct config { config format bind [addr]:[port] { + ssl { + key = file + cert = certfile + } site { hostname|alias = "[host]" root = "/" diff --git a/src/ssl.h b/src/ssl.h index 31a3075..725dcb4 100644 --- a/src/ssl.h +++ b/src/ssl.h @@ -15,8 +15,8 @@ struct ssl_connection { }; struct ssl_settings { - const char* privateKey; - const char* certificate; + char* privateKey; + char* certificate; struct { SSL_CTX* ctx; } _private; diff --git a/src/test.c b/src/test.c index 0b37f19..736a542 100644 --- a/src/test.c +++ b/src/test.c @@ -238,10 +238,24 @@ void testHeaders() { } void testConfig() { - struct config* config = config_parse(fopen("test.conf", "r")); + FILE* file; + #ifdef SSL_SUPPORT + file = fopen("test-with-ssl.conf", "r"); + #else + file = fopen("test.conf", "r"); + #endif + + struct config* config = config_parse(file); + checkNull(config, "null check"); - checkInt(config->nrBinds, 1, "bind no check"); + + #ifdef SSL_SUPPORT + checkInt(config->nrBinds, 2, "bind no check"); + #else + checkInt(config->nrBinds, 1, "bind no check"); + #endif + checkString(config->binds[0]->addr, "0.0.0.0", "bind addr check"); checkString(config->binds[0]->port, "80", "bind port check"); checkInt(config->binds[0]->nrSites, 1, "site no check"); @@ -256,7 +270,27 @@ void testConfig() { checkInt(config->binds[0]->sites[0]->handlers[0]->settings.fileSettings.indexfiles.number, 1, "handler settings index no"); checkString(config->binds[0]->sites[0]->handlers[0]->settings.fileSettings.indexfiles.files[0], "index.html", "handler settings index check"); - + #ifdef SSL_SUPPORT + checkString(config->binds[1]->addr, "0.0.0.0", "bind addr check"); + checkString(config->binds[1]->port, "443", "bind port check"); + checkNull(config->binds[1]->ssl, "ssl null check"); + checkString(config->binds[1]->ssl->privateKey, "ssl.key", "ssl key check"); + checkString(config->binds[1]->ssl->certificate, "ssl.crt", "ssl cert check"); + checkInt(config->binds[1]->nrSites, 1, "site no check"); + checkInt(config->binds[1]->sites[0]->nrHostnames, 1, "site hostname no check"); + checkString(config->binds[1]->sites[0]->hostnames[0], "example.com", "site hostname check"); + checkString(config->binds[1]->sites[0]->documentRoot, "/var/www", "site document root check"); + checkInt(config->binds[1]->sites[0]->nrHandlers, 1, "handler no check"); + checkString(config->binds[1]->sites[0]->handlers[0]->dir, "/", "handler dir check"); + checkInt(config->binds[1]->sites[0]->handlers[0]->type, FILE_HANDLER_NO, "handler type no check"); + checkVoid(config->binds[1]->sites[0]->handlers[0]->handler, &fileHandler, "handler ptr check"); + checkString(config->binds[1]->sites[0]->handlers[0]->settings.fileSettings.documentRoot, "/var/www", "handler settings root check"); + checkInt(config->binds[1]->sites[0]->handlers[0]->settings.fileSettings.indexfiles.number, 1, "handler settings index no"); + checkString(config->binds[1]->sites[0]->handlers[0]->settings.fileSettings.indexfiles.files[0], "index.html", "handler settings index check"); + #endif + + fclose(file); + config_destroy(config); } diff --git a/test-with-ssl.conf b/test-with-ssl.conf new file mode 100644 index 0000000..b2e28cc --- /dev/null +++ b/test-with-ssl.conf @@ -0,0 +1,24 @@ +bind 0.0.0.0:80 { + site { + hostname = example.com + root = /var/www + handler / { + type = file + index = index.html + } + } +} +bind 0.0.0.0:443 { + ssl { + key = ssl.key + cert = ssl.crt + } + site { + hostname = example.com + root = /var/www + handler / { + type = file + index = index.html + } + } +}