kore

An easy to use, scalable and secure web application framework for writing web APIs in C.
Commits | Files | Refs | README | LICENSE | git clone https://git.kore.io/kore.git

commit 6026a6d4ee961621e27ec692ff9eaf007263e806
parent a1b400c400897d106bc61ab6113df26ab4f11bcc
Author: Joris Vink <joris@coders.se>
Date:   Mon, 24 Jun 2013 11:32:45 +0200

add SNI support, and change domain configuration a bit.

Diffstat:
Makefile | 2+-
example.conf | 24+++++++++++++++++-------
includes/kore.h | 23++++++++++++++++-------
src/accesslog.c | 6+++---
src/config.c | 94+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
src/domain.c | 130+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/kore.c | 82++++++++++++++++++++++++++++++++++---------------------------------------------
src/module.c | 51+++++----------------------------------------------
8 files changed, 266 insertions(+), 146 deletions(-)

diff --git a/Makefile b/Makefile @@ -4,7 +4,7 @@ CC=gcc BIN=kore S_SRC+= src/kore.c src/buf.c src/config.c src/net.c src/spdy.c src/http.c \ - src/accesslog.c src/module.c src/utils.c src/zlib_dict.c + src/accesslog.c src/domain.c src/module.c src/utils.c src/zlib_dict.c S_OBJS= $(S_SRC:.c=.o) CFLAGS+=-I/usr/local/ssl/include diff --git a/example.conf b/example.conf @@ -2,8 +2,6 @@ # Server configuration. bind 10.211.55.3 443 -certfile cert/server.crt -certkey cert/server.key # The path worker processes will chroot too after starting. chroot /home/joris/src/kore @@ -42,8 +40,20 @@ load example/example.module # handler path module_callback # Example domain that responds to 10.211.55.33. -domain 10.211.55.3 -accesslog /var/log/kore_access.log -static /css/style.css serve_style_css -static / serve_index -dynamic ^/[a-z0-9_]*$ serve_profile +domain 10.211.55.3 { + certfile cert/server.crt + certkey cert/server.key + accesslog /var/log/kore_access.log + static /css/style.css serve_style_css + static / serve_index + dynamic ^/[a-z0-9_]*$ serve_profile +} + +#domain domain.com { +# certfile cert/other/server.crt +# certkey cert/other/server.key +# accesslog /var/log/other_kore_access.log +# static /css/style.css serve_style_css +# static / serve_index +# dynamic ^/[a-z0-9_]*$ serve_profile +#} diff --git a/includes/kore.h b/includes/kore.h @@ -114,13 +114,18 @@ struct kore_worker { TAILQ_HEAD(kore_worker_h, kore_worker); -struct module_domain { +struct kore_domain { char *domain; + char *certfile; + char *certkey; int accesslog; + SSL_CTX *ssl_ctx; TAILQ_HEAD(, kore_module_handle) handlers; - TAILQ_ENTRY(module_domain) list; + TAILQ_ENTRY(kore_domain) list; }; +TAILQ_HEAD(kore_domain_h, kore_domain); + #define KORE_BUF_INITIAL 128 #define KORE_BUF_INCREMENT KORE_BUF_INITIAL @@ -143,8 +148,6 @@ extern char *chroot_path; extern char *runas_user; extern char *kore_module_onload; extern char *kore_pidfile; -extern char *kore_certfile; -extern char *kore_certkey; extern u_int16_t cpu_count; extern u_int8_t worker_count; @@ -152,6 +155,8 @@ extern u_int8_t worker_count; extern struct listener server; extern struct kore_worker *worker; extern struct kore_worker_h kore_workers; +extern struct kore_domain_h domains; +extern struct kore_domain *primary_dom; void kore_worker_init(void); void kore_worker_wait(int); @@ -168,6 +173,8 @@ void kore_worker_setcpu(struct kore_worker *); void kore_event_schedule(int, int, int, void *); int kore_connection_handle(struct connection *); int kore_server_accept(struct listener *, struct connection **); +int kore_ssl_sni_cb(SSL *, int *, void *); +int kore_ssl_npn_cb(SSL *, const u_char **, unsigned int *, void *); u_int64_t kore_time_ms(void); void kore_log_init(void); @@ -184,14 +191,16 @@ void kore_server_disconnect(struct connection *); int kore_split_string(char *, char *, char **, size_t); long long kore_strtonum(const char *, long long, long long, int *); +void kore_domain_init(void); +int kore_domain_new(char *); void kore_module_load(char *); void kore_module_reload(void); int kore_module_loaded(void); -int kore_module_domain_new(char *); -void kore_module_domain_closelogs(void); +void kore_domain_closelogs(void); void *kore_module_handler_find(char *, char *); +void kore_domain_sslstart(struct kore_domain *); int kore_module_handler_new(char *, char *, char *, int); -struct module_domain *kore_module_domain_lookup(char *); +struct kore_domain *kore_domain_lookup(const char *); void fatal(const char *, ...); void kore_debug_internal(char *, int, const char *, ...); diff --git a/src/accesslog.c b/src/accesslog.c @@ -65,7 +65,7 @@ void kore_accesslog_worker_init(void) { close(accesslog_fd[0]); - kore_module_domain_closelogs(); + kore_domain_closelogs(); } int @@ -75,7 +75,7 @@ kore_accesslog_wait(void) time_t now; size_t slen; int nfds; - struct module_domain *dom; + struct kore_domain *dom; struct pollfd pfd[1]; struct kore_log_packet logpacket; char *method, buf[4096], *tbuf; @@ -104,7 +104,7 @@ kore_accesslog_wait(void) if (len != sizeof(logpacket)) return (KORE_RESULT_ERROR); - if ((dom = kore_module_domain_lookup(logpacket.host)) == NULL) { + if ((dom = kore_domain_lookup(logpacket.host)) == NULL) { kore_log(LOG_WARNING, "got accesslog packet for unknown domain: %s", logpacket.host); diff --git a/src/config.c b/src/config.c @@ -47,9 +47,10 @@ static int configure_chroot(char **); static int configure_runas(char **); static int configure_workers(char **); static int configure_pidfile(char **); +static int configure_accesslog(char **); static int configure_certfile(char **); static int configure_certkey(char **); -static int configure_accesslog(char **); +static void domain_sslstart(void); static struct { const char *name; @@ -65,13 +66,13 @@ static struct { { "runas", configure_runas }, { "workers", configure_workers }, { "pidfile", configure_pidfile }, + { "accesslog", configure_accesslog }, { "certfile", configure_certfile }, { "certkey", configure_certkey }, - { "accesslog", configure_accesslog }, { NULL, NULL }, }; -static char *current_domain = NULL; +static struct kore_domain *current_domain = NULL; void kore_parse_config(const char *config_path) @@ -100,6 +101,9 @@ kore_parse_config(const char *config_path) *t = ' '; } + if (!strcmp(p, "}") && current_domain != NULL) + domain_sslstart(); + kore_split_string(p, " ", argv, 5); for (i = 0; config_names[i].name != NULL; i++) { if (!strcmp(config_names[i].name, argv[0])) { @@ -165,17 +169,25 @@ configure_onload(char **argv) static int configure_domain(char **argv) { - if (argv[1] == NULL) + if (argv[2] == NULL) return (KORE_RESULT_ERROR); - if (current_domain != NULL) - free(current_domain); - current_domain = kore_strdup(argv[1]); - if (!kore_module_domain_new(current_domain)) { + if (current_domain != NULL) { + kore_debug("previous domain configuration not closed"); + return (KORE_RESULT_ERROR); + } + + if (strcmp(argv[2], "{")) { + kore_debug("missing { for domain directive"); + return (KORE_RESULT_ERROR); + } + + if (!kore_domain_new(argv[1])) { kore_debug("could not create new domain %s", current_domain); return (KORE_RESULT_ERROR); } + current_domain = kore_domain_lookup(argv[1]); return (KORE_RESULT_OK); } @@ -199,7 +211,8 @@ configure_handler(char **argv) else return (KORE_RESULT_ERROR); - if (!kore_module_handler_new(argv[1], current_domain, argv[2], type)) { + if (!kore_module_handler_new(argv[1], + current_domain->domain, argv[2], type)) { kore_debug("cannot create handler for %s", argv[1]); return (KORE_RESULT_ERROR); } @@ -275,64 +288,75 @@ configure_pidfile(char **argv) } static int -configure_certfile(char **argv) +configure_accesslog(char **argv) { if (argv[1] == NULL) return (KORE_RESULT_ERROR); - if (kore_certfile != NULL) { - kore_debug("duplicate kore_certfile directive specified"); + if (current_domain == NULL) { + kore_debug("missing domain for accesslog"); return (KORE_RESULT_ERROR); } - kore_certfile = kore_strdup(argv[1]); - return (KORE_RESULT_OK); -} - -static int -configure_certkey(char **argv) -{ - if (argv[1] == NULL) + if (current_domain->accesslog != -1) { + kore_debug("domain %s already has an open accesslog", + current_domain->domain); return (KORE_RESULT_ERROR); + } - if (kore_certkey != NULL) { - kore_debug("duplicate kore_certkey directive specified"); + current_domain->accesslog = open(argv[1], + O_CREAT | O_APPEND | O_WRONLY, 0755); + if (current_domain->accesslog == -1) { + kore_debug("open(%s): %s", argv[1], errno_s); return (KORE_RESULT_ERROR); } - kore_certkey = kore_strdup(argv[1]); return (KORE_RESULT_OK); } static int -configure_accesslog(char **argv) +configure_certfile(char **argv) { - struct module_domain *dom; - if (argv[1] == NULL) return (KORE_RESULT_ERROR); if (current_domain == NULL) { - kore_debug("missing domain for accesslog"); + kore_debug("missing domain for certfile"); return (KORE_RESULT_ERROR); } - if ((dom = kore_module_domain_lookup(current_domain)) == NULL) { - kore_debug("current_domain not found: (%s)", current_domain); + if (current_domain->certfile != NULL) { + kore_debug("domain already has a certfile set"); return (KORE_RESULT_ERROR); } - if (dom->accesslog != -1) { - kore_debug("domain %s already has an open accesslog", - current_domain); + current_domain->certfile = kore_strdup(argv[1]); + return (KORE_RESULT_OK); +} + +static int +configure_certkey(char **argv) +{ + if (argv[1] == NULL) + return (KORE_RESULT_ERROR); + + if (current_domain == NULL) { + kore_debug("missing domain for certkey"); return (KORE_RESULT_ERROR); } - dom->accesslog = open(argv[1], O_CREAT | O_APPEND | O_WRONLY, 0755); - if (dom->accesslog == -1) { - kore_debug("open(%s): %s", argv[1], errno_s); + if (current_domain->certkey != NULL) { + kore_debug("domain already has a certkey set"); return (KORE_RESULT_ERROR); } + current_domain->certkey = kore_strdup(argv[1]); return (KORE_RESULT_OK); } + +static void +domain_sslstart(void) +{ + kore_domain_sslstart(current_domain); + current_domain = NULL; +} diff --git a/src/domain.c b/src/domain.c @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2013 Joris Vink <joris@coders.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/queue.h> + +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <openssl/err.h> +#include <openssl/ssl.h> + +#include <ctype.h> +#include <dlfcn.h> +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <regex.h> +#include <zlib.h> + +#include "spdy.h" +#include "kore.h" + +struct kore_domain_h domains; +struct kore_domain *primary_dom = NULL; + +void +kore_domain_init(void) +{ + TAILQ_INIT(&domains); +} + +int +kore_domain_new(char *domain) +{ + struct kore_domain *dom; + + if (kore_domain_lookup(domain) != NULL) + return (KORE_RESULT_ERROR); + + kore_debug("kore_domain_new(%s, %s, %s)", domain); + + dom = (struct kore_domain *)kore_malloc(sizeof(*dom)); + dom->accesslog = -1; + dom->certfile = NULL; + dom->certkey = NULL; + dom->domain = kore_strdup(domain); + TAILQ_INIT(&(dom->handlers)); + TAILQ_INSERT_TAIL(&domains, dom, list); + + if (primary_dom == NULL) + primary_dom = dom; + + return (KORE_RESULT_OK); +} + +void +kore_domain_sslstart(struct kore_domain *dom) +{ + kore_debug("kore_domain_sslstart(%s)", dom->domain); + + dom->ssl_ctx = SSL_CTX_new(SSLv23_server_method()); + if (dom->ssl_ctx == NULL) + fatal("kore_domain_new(): SSL_ctx_new(): %s", ssl_errno_s); + if (!SSL_CTX_use_certificate_chain_file(dom->ssl_ctx, dom->certfile)) { + fatal("SSL_CTX_use_certificate_chain_file(%s): %s", + dom->certfile, ssl_errno_s); + } + + if (!SSL_CTX_use_PrivateKey_file(dom->ssl_ctx, dom->certkey, + SSL_FILETYPE_PEM)) { + fatal("SSL_CTX_use_PrivateKey_file(%s): %s", + dom->certkey, ssl_errno_s); + } + + if (!SSL_CTX_check_private_key(dom->ssl_ctx)) + fatal("Public/Private key for %s do not match", dom->domain); + + SSL_CTX_set_mode(dom->ssl_ctx, SSL_MODE_AUTO_RETRY); + SSL_CTX_set_mode(dom->ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); + SSL_CTX_set_options(dom->ssl_ctx, SSL_OP_NO_SSLv2); + SSL_CTX_set_tlsext_servername_callback(dom->ssl_ctx, kore_ssl_sni_cb); + SSL_CTX_set_next_protos_advertised_cb(dom->ssl_ctx, + kore_ssl_npn_cb, NULL); + + free(dom->certfile); + free(dom->certkey); +} + +struct kore_domain * +kore_domain_lookup(const char *domain) +{ + struct kore_domain *dom; + + TAILQ_FOREACH(dom, &domains, list) { + if (!strcmp(dom->domain, domain)) + return (dom); + } + + return (NULL); +} + +void +kore_domain_closelogs(void) +{ + struct kore_domain *dom; + + TAILQ_FOREACH(dom, &domains, list) + close(dom->accesslog); +} diff --git a/src/kore.c b/src/kore.c @@ -46,8 +46,6 @@ #include "kore.h" #include "http.h" -static SSL_CTX *ssl_ctx = NULL; - volatile sig_atomic_t sig_recv; static TAILQ_HEAD(, connection) disconnected; static TAILQ_HEAD(, connection) worker_clients; @@ -65,18 +63,15 @@ u_int8_t worker_count = 0; char *server_ip = NULL; char *runas_user = NULL; char *chroot_path = NULL; -char *kore_certkey = NULL; -char *kore_certfile = NULL; char *kore_pidfile = KORE_PIDFILE_DEFAULT; static void usage(void); static void kore_signal(int); static void kore_write_mypid(void); static int kore_socket_nonblock(int); -static int kore_server_sslstart(void); +static void kore_server_sslstart(void); static void kore_server_final_disconnect(struct connection *); static int kore_server_bind(struct listener *, const char *, int); -static int kore_ssl_npn_cb(SSL *, const u_char **, unsigned int *, void *); static void usage(void) @@ -117,6 +112,10 @@ main(int argc, char *argv[]) fatal("please specify a configuration file to use (-c)"); mypid = getpid(); + + kore_domain_init(); + kore_server_sslstart(); + kore_parse_config(config_file); if (!kore_module_loaded()) fatal("no site module was loaded"); @@ -129,15 +128,11 @@ main(int argc, char *argv[]) fatal("missing a username to run as"); if ((pw = getpwnam(runas_user)) == NULL) fatal("user '%s' does not exist", runas_user); - if (kore_certfile == NULL || kore_certkey == NULL) - fatal("missing certificate information"); kore_log_init(); kore_platform_init(); kore_accesslog_init(); - if (!kore_server_sslstart()) - fatal("cannot initiate SSL"); if (!kore_server_bind(&server, server_ip, server_port)) fatal("cannot bind to %s:%d", server_ip, server_port); if (daemon(1, 1) == -1) @@ -156,8 +151,6 @@ main(int argc, char *argv[]) free(server_ip); free(runas_user); - free(kore_certkey); - free(kore_certfile); for (;;) { if (sig_recv != 0) { @@ -261,7 +254,7 @@ kore_connection_handle(struct connection *c) switch (c->state) { case CONN_STATE_SSL_SHAKE: if (c->ssl == NULL) { - c->ssl = SSL_new(ssl_ctx); + c->ssl = SSL_new(primary_dom->ssl_ctx); if (c->ssl == NULL) { kore_debug("SSL_new(): %s", ssl_errno_s); return (KORE_RESULT_ERROR); @@ -429,36 +422,42 @@ kore_worker_entry(struct kore_worker *kw) exit(0); } -static int -kore_server_sslstart(void) +int +kore_ssl_npn_cb(SSL *ssl, const u_char **data, unsigned int *len, void *arg) { - kore_debug("kore_server_sslstart()"); + kore_debug("kore_ssl_npn_cb(): sending protocols"); - SSL_library_init(); - SSL_load_error_strings(); - ssl_ctx = SSL_CTX_new(SSLv23_server_method()); - if (ssl_ctx == NULL) { - kore_debug("SSL_ctx_new(): %s", ssl_errno_s); - return (KORE_RESULT_ERROR); - } + *data = (const unsigned char *)KORE_SSL_PROTO_STRING; + *len = strlen(KORE_SSL_PROTO_STRING); - if (!SSL_CTX_use_certificate_chain_file(ssl_ctx, kore_certfile)) { - kore_debug("SSL_CTX_use_certificate_file(): %s", ssl_errno_s); - return (KORE_RESULT_ERROR); - } + return (SSL_TLSEXT_ERR_OK); +} - if (!SSL_CTX_use_PrivateKey_file(ssl_ctx, kore_certkey, - SSL_FILETYPE_PEM)) { - kore_debug("SSL_CTX_use_PrivateKey_file(): %s", ssl_errno_s); - return (KORE_RESULT_ERROR); +int +kore_ssl_sni_cb(SSL *ssl, int *ad, void *arg) +{ + struct kore_domain *dom; + const char *sname; + + sname = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + kore_debug("kore_ssl_sni_cb(): received host %s", sname); + + if (sname != NULL && (dom = kore_domain_lookup(sname)) != NULL) { + kore_debug("kore_ssl_sni_cb(): Using %s CTX", sname); + SSL_set_SSL_CTX(ssl, dom->ssl_ctx); + return (SSL_TLSEXT_ERR_OK); } - SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); - SSL_CTX_set_mode(ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); - SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2); - SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, kore_ssl_npn_cb, NULL); + return (SSL_TLSEXT_ERR_NOACK); +} - return (KORE_RESULT_OK); +static void +kore_server_sslstart(void) +{ + kore_debug("kore_server_sslstart()"); + + SSL_library_init(); + SSL_load_error_strings(); } static int @@ -575,17 +574,6 @@ kore_socket_nonblock(int fd) return (KORE_RESULT_OK); } -static int -kore_ssl_npn_cb(SSL *ssl, const u_char **data, unsigned int *len, void *arg) -{ - kore_debug("kore_ssl_npn_cb(): sending protocols"); - - *data = (const unsigned char *)KORE_SSL_PROTO_STRING; - *len = strlen(KORE_SSL_PROTO_STRING); - - return (SSL_TLSEXT_ERR_OK); -} - static void kore_write_mypid(void) { diff --git a/src/module.c b/src/module.c @@ -46,8 +46,6 @@ static char *mod_name = NULL; static time_t mod_last_mtime = 0; char *kore_module_onload = NULL; -static TAILQ_HEAD(, module_domain) domains; - void kore_module_load(char *module_name) { @@ -81,7 +79,7 @@ kore_module_load(char *module_name) void kore_module_reload(void) { - struct module_domain *dom; + struct kore_domain *dom; struct kore_module_handle *hdlr; void (*onload)(void); @@ -117,27 +115,10 @@ kore_module_loaded(void) } int -kore_module_domain_new(char *domain) -{ - struct module_domain *dom; - - if (kore_module_domain_lookup(domain) != NULL) - return (KORE_RESULT_ERROR); - - dom = (struct module_domain *)kore_malloc(sizeof(*dom)); - dom->accesslog = -1; - dom->domain = kore_strdup(domain); - TAILQ_INIT(&(dom->handlers)); - TAILQ_INSERT_TAIL(&domains, dom, list); - - return (KORE_RESULT_OK); -} - -int kore_module_handler_new(char *path, char *domain, char *func, int type) { void *addr; - struct module_domain *dom; + struct kore_domain *dom; struct kore_module_handle *hdlr; kore_debug("kore_module_handler_new(%s, %s, %s, %d)", path, @@ -149,7 +130,7 @@ kore_module_handler_new(char *path, char *domain, char *func, int type) return (KORE_RESULT_ERROR); } - if ((dom = kore_module_domain_lookup(domain)) == NULL) + if ((dom = kore_domain_lookup(domain)) == NULL) return (KORE_RESULT_ERROR); hdlr = (struct kore_module_handle *)kore_malloc(sizeof(*hdlr)); @@ -175,10 +156,10 @@ kore_module_handler_new(char *path, char *domain, char *func, int type) void * kore_module_handler_find(char *domain, char *path) { - struct module_domain *dom; + struct kore_domain *dom; struct kore_module_handle *hdlr; - if ((dom = kore_module_domain_lookup(domain)) == NULL) + if ((dom = kore_domain_lookup(domain)) == NULL) return (NULL); TAILQ_FOREACH(hdlr, &(dom->handlers), list) { @@ -193,25 +174,3 @@ kore_module_handler_find(char *domain, char *path) return (NULL); } - -struct module_domain * -kore_module_domain_lookup(char *domain) -{ - struct module_domain *dom; - - TAILQ_FOREACH(dom, &domains, list) { - if (!strcmp(dom->domain, domain)) - return (dom); - } - - return (NULL); -} - -void -kore_module_domain_closelogs(void) -{ - struct module_domain *dom; - - TAILQ_FOREACH(dom, &domains, list) - close(dom->accesslog); -}