diff --git a/BEERS b/BEERS index 648ef5e..0a1fb4d 100644 --- a/BEERS +++ b/BEERS @@ -15,6 +15,7 @@ I will note down the beer of your choice between the brackets. [] Cleve Lendon [] Corbin Hughes [] Daniel Fahlgren x 6 +[] Daniel Melani [] Dmitrii Golub [] Elliot Schlegelmilch [] Erik Karlsson x 2 diff --git a/Makefile b/Makefile index f7ae72a..9ea1c8f 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,6 @@ KODEV=kodev/kodev KOREPATH?=$(shell pwd) KORE_CRYPTO?=crypto INSTALL_DIR=$(PREFIX)/bin -MAN_DIR?=$(PREFIX)/share/man SHARE_DIR=$(PREFIX)/share/kore INCLUDE_DIR=$(PREFIX)/include/kore TLS_BACKEND?=openssl @@ -118,6 +117,16 @@ ifneq ("$(PYTHON)", "") FEATURES_INC+=$(KORE_PYTHON_INC) endif +ifneq ("$(LUA)", "") + S_SRC+=src/lua.c + KORE_LUA_LIB?=$(shell pkg-config --libs lua$(LUA_VERSION)) + KORE_LUA_INC?=$(shell pkg-config --cflags lua$(LUA_VERSION)) + LDFLAGS+=$(KORE_LUA_LIB) + CFLAGS+=$(KORE_LUA_INC) -DKORE_USE_LUA + FEATURES+=-DKORE_USE_LUA + FEATURES_INC+=$(KORE_LUA_INC) +endif + OSNAME=$(shell uname -s | sed -e 's/[-_].*//g' | tr A-Z a-z) ifeq ("$(OSNAME)", "freebsd") KORE_CURL_LIB=-L/usr/local/lib -lcurl @@ -215,8 +224,6 @@ install: mkdir -p $(DESTDIR)$(SHARE_DIR) mkdir -p $(DESTDIR)$(INCLUDE_DIR) mkdir -p $(DESTDIR)$(INSTALL_DIR) - mkdir -p $(DESTDIR)$(MAN_DIR)/man1 - install -m 644 share/man/kodev.1 $(DESTDIR)$(MAN_DIR)/man1/kodev.1 install -m 555 $(KORE) $(DESTDIR)$(INSTALL_DIR)/$(KORE) install -m 644 kore.features $(DESTDIR)$(SHARE_DIR)/features install -m 644 kore.linker $(DESTDIR)$(SHARE_DIR)/linker diff --git a/README.md b/README.md index 7eca392..b246924 100644 --- a/README.md +++ b/README.md @@ -59,11 +59,10 @@ Kore only supports x64, arm and aarch64 architectures. Building Kore ------------- -Clone this repository or get the latest release at [https://kore.io/releases/4.2.2](https://kore.io/releases/4.2.2). +Clone this repository or get the latest release at [https://kore.io/releases/4.2.3](https://kore.io/releases/4.2.3). Requirements -* openssl 1.1.1 or libressl 3.x -(note: openssl 3.0.0 is currently *not* supported) +* openssl 1.1.1, libressl 3.x or openssl 3. Requirement for asynchronous curl (optional) * libcurl (7.64.0 or higher) @@ -77,6 +76,9 @@ Requirements for pgsql (optional) Requirements for python (optional) * Python 3.6+ +Requirements for lua support (optional) +* Lua 5.4+ + Normal compilation and installation: ``` @@ -88,6 +90,7 @@ $ make If you would like to build a specific flavor, you can enable those by setting a shell environment variable before running **_make_**. +* LUA=1 (compiles in LUA support) * ACME=1 (compiles in ACME support) * CURL=1 (compiles in asynchronous curl support) * TASKS=1 (compiles in task support) diff --git a/examples/tasks/src/tasks.c b/examples/tasks/src/tasks.c index e21a6ea..a7ee1ce 100644 --- a/examples/tasks/src/tasks.c +++ b/examples/tasks/src/tasks.c @@ -55,6 +55,8 @@ KORE_SECCOMP_FILTER("tasks", KORE_SYSCALL_ALLOW(recvmsg), KORE_SYSCALL_ALLOW(sendmmsg), KORE_SYSCALL_ALLOW(getpeername), + KORE_SYSCALL_ALLOW(rseq), + KORE_SYSCALL_ALLOW(clone3), ); #endif diff --git a/include/kore/http.h b/include/kore/http.h index c4538a2..c865750 100644 --- a/include/kore/http.h +++ b/include/kore/http.h @@ -415,8 +415,8 @@ int http_state_exists(struct http_request *); void http_state_cleanup(struct http_request *); void *http_state_create(struct http_request *, size_t); -int http_argument_urldecode(char *); int http_header_recv(struct netbuf *); +int http_argument_urldecode(char *, int); void http_populate_qs(struct http_request *); void http_populate_post(struct http_request *); void http_populate_multipart_form(struct http_request *); diff --git a/include/kore/kore.h b/include/kore/kore.h index 3977b45..c09d8eb 100644 --- a/include/kore/kore.h +++ b/include/kore/kore.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -273,9 +274,11 @@ extern struct connection_list disconnected; #define KORE_RUNTIME_NATIVE 0 #define KORE_RUNTIME_PYTHON 1 +#define KORE_RUNTIME_LUA 2 struct kore_runtime { int type; + int (*resolve)(const char *, const struct stat *); #if !defined(KORE_NO_HTTP) int (*http_request)(void *, struct http_request *); void (*http_request_free)(void *, struct http_request *); @@ -411,8 +414,9 @@ struct kore_auth { #define KORE_MODULE_LOAD 1 #define KORE_MODULE_UNLOAD 2 -#define KORE_MODULE_NATIVE 0 -#define KORE_MODULE_PYTHON 1 +#define KORE_MODULE_NATIVE KORE_RUNTIME_NATIVE +#define KORE_MODULE_PYTHON KORE_RUNTIME_PYTHON +#define KORE_MODULE_LUA KORE_RUNTIME_LUA struct kore_module; @@ -607,29 +611,24 @@ struct kore_json_item { TAILQ_ENTRY(kore_json_item) list; }; -struct kore_pool_region { - void *start; - size_t length; - LIST_ENTRY(kore_pool_region) list; -}; - struct kore_pool_entry { u_int8_t state; - struct kore_pool_region *region; - LIST_ENTRY(kore_pool_entry) list; + void *uptr; + void *canary; + struct kore_pool_entry *nextfree; }; struct kore_pool { - size_t elen; - size_t slen; - size_t elms; - size_t inuse; + size_t memsz; size_t growth; + size_t pagesz; + size_t elmlen; + size_t uselen; + u_int64_t canary; volatile int lock; char *name; - LIST_HEAD(, kore_pool_region) regions; - LIST_HEAD(, kore_pool_entry) freelist; + struct kore_pool_entry *freelist; }; struct kore_timer { @@ -708,6 +707,7 @@ extern int kore_quit; extern int kore_quiet; extern int skip_chroot; extern int skip_runas; +extern int kore_mem_guard; extern int kore_foreground; extern char *kore_pidfile; @@ -753,6 +753,7 @@ void kore_server_closeall(void); void kore_server_cleanup(void); void kore_server_free(struct kore_server *); void kore_server_finalize(struct kore_server *); +void kore_hooks_set(const char *, const char *, const char *); struct kore_server *kore_server_create(const char *); struct kore_server *kore_server_lookup(const char *); @@ -803,6 +804,7 @@ void kore_platform_schedule_read(int, void *); void kore_platform_schedule_write(int, void *); void kore_platform_event_schedule(int, int, int, void *); void kore_platform_worker_setcpu(struct kore_worker *); +u_int32_t kore_platform_random_uint32(void); #if defined(KORE_USE_PLATFORM_SENDFILE) int kore_platform_sendfile(struct connection *, struct netbuf *); @@ -822,6 +824,7 @@ void kore_tls_dh_check(void); int kore_tls_supported(void); void kore_tls_version_set(int); void kore_tls_keymgr_init(void); +void kore_tls_log_version(void); int kore_tls_dh_load(const char *); void kore_tls_seed(const void *, size_t); int kore_tls_ciphersuite_set(const char *); @@ -885,24 +888,27 @@ void kore_connection_check_idletimer(u_int64_t, struct connection *); int kore_connection_accept(struct listener *, struct connection **); +void kore_connection_log(struct connection *, + const char *, ...) + __attribute__((format (printf, 2, 3))); +const char *kore_connection_ip(struct connection *); void kore_log_init(void); void kore_log_file(const char *); -#if defined(KORE_USE_PYTHON) -int kore_configure_setting(const char *, char *); -#endif - /* config.c */ void kore_parse_config(void); void kore_parse_config_file(FILE *); +int kore_configure_setting(const char *, char *); /* mem.c */ void *kore_malloc(size_t); +void *kore_mmap_region(size_t); void *kore_calloc(size_t, size_t); void *kore_realloc(void *, size_t); void kore_free(void *); void kore_mem_init(void); +void kore_free_zero(void *); void kore_mem_cleanup(void); void kore_mem_untag(void *); void *kore_mem_lookup(u_int32_t); @@ -933,7 +939,7 @@ u_int64_t kore_strtonum64(const char *, int, int *); size_t kore_strlcpy(char *, const char *, const size_t); void kore_server_disconnect(struct connection *); int kore_split_string(char *, const char *, char **, size_t); -void kore_strip_chars(char *, const char, char **); +void kore_strip_chars(const char *, const char, char **); int kore_snprintf(char *, size_t, int *, const char *, ...) __attribute__((format (printf, 4, 5))); long long kore_strtonum(const char *, int, long long, long long, int *); @@ -1022,6 +1028,7 @@ int kore_route_lookup(struct http_request *, #endif /* runtime.c */ +const size_t kore_runtime_count(void); struct kore_runtime_call *kore_runtime_getcall(const char *); struct kore_module *kore_module_load(const char *, const char *, int); @@ -1029,6 +1036,7 @@ struct kore_module *kore_module_load(const char *, void kore_runtime_execute(struct kore_runtime_call *); int kore_runtime_onload(struct kore_runtime_call *, int); void kore_runtime_signal(struct kore_runtime_call *, int); +void kore_runtime_resolve(const char *, const struct stat *); void kore_runtime_configure(struct kore_runtime_call *, int, char **); void kore_runtime_connect(struct kore_runtime_call *, struct connection *); #if !defined(KORE_NO_HTTP) diff --git a/include/kore/lua_api.h b/include/kore/lua_api.h new file mode 100644 index 0000000..fa33e22 --- /dev/null +++ b/include/kore/lua_api.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2023 Joris Vink + * + * 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. + */ + +#ifndef __H_LUA_API_H +#define __H_LUA_API_H + +#include +#include +#include + +void kore_lua_init(void); +void kore_lua_cleanup(void); + +extern struct kore_module_functions kore_lua_module; +extern struct kore_runtime kore_lua_runtime; + +#endif diff --git a/include/kore/lua_methods.h b/include/kore/lua_methods.h new file mode 100644 index 0000000..5dc660f --- /dev/null +++ b/include/kore/lua_methods.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2023 Joris Vink + * + * 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. + */ + +#ifndef __H_LUA_METHODS_H +#define __H_LUA_METHODS_H + +static int lua_http_request_gc(lua_State *); +static int lua_http_request_index(lua_State *); + +static int lua_http_response(lua_State *); +static int lua_http_request_header(lua_State *); +static int lua_http_response_header(lua_State *); + +static const luaL_Reg lua_http_request_meta[] = { + { "__gc", lua_http_request_gc }, + { "__index", lua_http_request_index }, + { NULL, NULL }, +}; + +static const luaL_Reg lua_http_request_methods[] = { + { "response", lua_http_response }, + { "request_header", lua_http_request_header }, + { "response_header", lua_http_response_header }, + { NULL, NULL }, +}; + +static int lua_kore_config(lua_State *); +static int lua_kore_server(lua_State *); + +static const luaL_Reg lua_kore_functions[] = { + { "config", lua_kore_config }, + { "server", lua_kore_server }, + { NULL, NULL }, +}; + +#endif diff --git a/include/kore/python_api.h b/include/kore/python_api.h index c1f25f8..ba6da31 100644 --- a/include/kore/python_api.h +++ b/include/kore/python_api.h @@ -44,10 +44,6 @@ void kore_python_seccomp_cleanup(void); void kore_python_seccomp_hook(const char *); #endif -#if !defined(KORE_SINGLE_BINARY) -extern const char *kore_pymodule; -#endif - extern struct kore_module_functions kore_python_module; extern struct kore_runtime kore_python_runtime; diff --git a/include/kore/python_methods.h b/include/kore/python_methods.h index 9e04224..0c06cff 100644 --- a/include/kore/python_methods.h +++ b/include/kore/python_methods.h @@ -39,7 +39,6 @@ static PyObject *python_kore_app(PyObject *, PyObject *); static PyObject *python_kore_log(PyObject *, PyObject *); static PyObject *python_kore_time(PyObject *, PyObject *); static PyObject *python_kore_lock(PyObject *, PyObject *); -static PyObject *python_kore_proc(PyObject *, PyObject *); static PyObject *python_kore_fatal(PyObject *, PyObject *); static PyObject *python_kore_queue(PyObject *, PyObject *); static PyObject *python_kore_worker(PyObject *, PyObject *); @@ -56,6 +55,7 @@ static PyObject *python_kore_task_kill(PyObject *, PyObject *); static PyObject *python_kore_prerequest(PyObject *, PyObject *); static PyObject *python_kore_task_create(PyObject *, PyObject *); static PyObject *python_kore_socket_wrap(PyObject *, PyObject *); +static PyObject *python_kore_proc(PyObject *, PyObject *, PyObject *); static PyObject *python_kore_route(PyObject *, PyObject *, PyObject *); static PyObject *python_kore_timer(PyObject *, PyObject *, PyObject *); static PyObject *python_kore_domain(PyObject *, PyObject *, PyObject *); @@ -92,7 +92,6 @@ static struct PyMethodDef pykore_methods[] = { METHOD("log", python_kore_log, METH_VARARGS), METHOD("time", python_kore_time, METH_NOARGS), METHOD("lock", python_kore_lock, METH_NOARGS), - METHOD("proc", python_kore_proc, METH_VARARGS), METHOD("queue", python_kore_queue, METH_VARARGS), METHOD("worker", python_kore_worker, METH_VARARGS), METHOD("tracer", python_kore_tracer, METH_VARARGS), @@ -109,6 +108,7 @@ static struct PyMethodDef pykore_methods[] = { METHOD("prerequest", python_kore_prerequest, METH_VARARGS), METHOD("task_create", python_kore_task_create, METH_VARARGS), METHOD("socket_wrap", python_kore_socket_wrap, METH_VARARGS), + METHOD("proc", python_kore_proc, METH_VARARGS | METH_KEYWORDS), METHOD("route", python_kore_route, METH_VARARGS | METH_KEYWORDS), METHOD("timer", python_kore_timer, METH_VARARGS | METH_KEYWORDS), METHOD("domain", python_kore_domain, METH_VARARGS | METH_KEYWORDS), @@ -231,10 +231,12 @@ struct pydomain { }; static PyObject *pydomain_filemaps(struct pydomain *, PyObject *); +static PyObject *pydomain_redirect(struct pydomain *, PyObject *); static PyObject *pydomain_route(struct pydomain *, PyObject *, PyObject *); static PyMethodDef pydomain_methods[] = { METHOD("filemaps", pydomain_filemaps, METH_VARARGS), + METHOD("redirect", pydomain_redirect, METH_VARARGS), METHOD("route", pydomain_route, METH_VARARGS | METH_KEYWORDS), METHOD(NULL, NULL, -1) }; @@ -560,6 +562,8 @@ static PyTypeObject pylock_op_type = { .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, }; +#define PYTHON_PROC_MAX_ENV 32 + struct pyproc { PyObject_HEAD pid_t pid; @@ -748,6 +752,7 @@ static void pyhttp_dealloc(struct pyhttp_request *); static void pyhttp_file_dealloc(struct pyhttp_file *); static PyObject *pyhttp_cookie(struct pyhttp_request *, PyObject *); +static PyObject *pyhttp_headers(struct pyhttp_request *, PyObject *); static PyObject *pyhttp_response(struct pyhttp_request *, PyObject *); static PyObject *pyhttp_argument(struct pyhttp_request *, PyObject *); static PyObject *pyhttp_body_read(struct pyhttp_request *, PyObject *); @@ -763,6 +768,7 @@ static PyObject *pyhttp_websocket_handshake(struct pyhttp_request *, static PyMethodDef pyhttp_request_methods[] = { METHOD("cookie", pyhttp_cookie, METH_VARARGS), + METHOD("headers", pyhttp_headers, METH_NOARGS), METHOD("response", pyhttp_response, METH_VARARGS), METHOD("argument", pyhttp_argument, METH_VARARGS), METHOD("body_read", pyhttp_body_read, METH_VARARGS), diff --git a/misc/hooks/post-receive b/misc/hooks/post-receive new file mode 100644 index 0000000..a0862e7 --- /dev/null +++ b/misc/hooks/post-receive @@ -0,0 +1,55 @@ +#!/usr/local/bin/bash + +set -x +set -e + +URL=discord-hook + +while read oldrev newrev ref; do + if [[ $ref =~ .*/master$ ]]; then + logmsg="" + commits=$(git rev-list ${oldrev}..${newrev}) + for commit in $commits; do + log=$(git log -1 --pretty=format:'[%h](https://git.kore.io/kore/commit/%H) %cn: %s' $commit) + logmsg="$logmsg $log\\n" + done + + curl -i \ + -H "Accept: application/json" \ + -H "Content-type: application/json" \ + -X POST \ + -d "{\"content\": \"${logmsg}\"}" \ + $URL + fi +done + +git update-server-info + +ROOT=/var/chroot/kore-site +TARGET=$ROOT/stagit +STATIC=$HOME/src/stagit_static +export TMPDIR=$ROOT/.tmp + +STAGING=`mktemp -d` + +function update_stagit { + mkdir -p $STAGING/$1 + pushd $STAGING/$1 + stagit $2 + cp log.html index.html + cp -R $2 ${STAGING}/${1}.git + rm ${STAGING}/${1}.git/hooks/post-receive + chmod -R +rx ${STAGING}/${1}.git + popd + +} + +update_stagit kore /home/git/kore.git + +cp -R $STATIC/* $STAGING +chmod -R o+rx $STAGING + +rm -rf $ROOT/.old +mv $TARGET $ROOT/.old +mv $STAGING $TARGET +rm -rf $ROOT/.old diff --git a/misc/kore-build/build-curl.sh b/misc/kore-build/build-curl.sh deleted file mode 100755 index a0234c2..0000000 --- a/misc/kore-build/build-curl.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh - -set -e - -. ./env.sh - -if [ $# -ne 3 ]; then - echo "Usage: build-curl.sh [release] [openssl] [nghttp2]" - exit 1 -fi - -export PKG_CONFIG="pkg-config --static" -export PKG_CONFIG_PATH="$FAKEROOT/openssl-$2/lib/pkgconfig:$FAKEROOT/nghttp2-$3/lib/pkgconfig" - -NAME=curl-$1 - -fetch "https://curl.haxx.se/download/$NAME.tar.gz" $NAME - -default_build $NAME --enable-shared=no diff --git a/misc/kore-build/build-kodev.sh b/misc/kore-build/build-kodev.sh deleted file mode 100755 index 80c0cd8..0000000 --- a/misc/kore-build/build-kodev.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh - -set -e - -. /home/build/env.sh - -if [ $# -ne 4 ]; then - echo "Usage: build-kore.sh [openssl] [python] [curl] [nghttp2]" - exit 1 -fi - -export PATH=$FAKEROOT/Python-$2/bin:$FAKEROOT/curl-$3/bin:$PATH -export OPENSSL_PATH=$FAKEROOT/openssl-$1 - -kodev clean -kodev build diff --git a/misc/kore-build/build-kore.sh b/misc/kore-build/build-kore.sh deleted file mode 100755 index cfd2db4..0000000 --- a/misc/kore-build/build-kore.sh +++ /dev/null @@ -1,67 +0,0 @@ -#!/bin/sh - -set -e - -if [ $# -ne 4 ]; then - echo "Usage: build-kore.sh [openssl] [python] [curl] [nghttp2]" - exit 1 -fi - -# Set ROOT based on the versions given. -VERSION=kore_ossl-$1_python-$2_curl-$3_nghttp2-$4 -ROOT=`pwd`/$VERSION - -# Pull in the rest of the functions. -. ./helpers.sh - -OPENSSL=openssl-$1 -PYTHON=Python-$2 -CURL=curl-$3 -NGHTTP2=nghttp2-$4 - -# Build OpenSSL -echo "Building $OPENSSL" -fetch "https://www.openssl.org/source/$OPENSSL.tar.gz" $OPENSSL -build $OPENSSL ./config no-shared --prefix=$FAKEROOT/$OPENSSL - -# Build Python -echo "Building $PYTHON" -fetch "https://www.python.org/ftp/python/$2/$PYTHON.tgz" $PYTHON -default_build $PYTHON - -# Build nghttp2 -echo "Building $NGHTTP2" -fetch \ - "https://github.com/nghttp2/nghttp2/releases/download/v$4/$NGHTTP2.tar.gz" \ - $NGHTTP2 - -default_build $NGHTTP2 --enable-lib-only --prefix=$FAKEROOT/$NGHTTP2 \ - --enable-shared=no - -# Build curl -echo "Building $CURL" -export PKG_CONFIG="pkg-config --static" -export PKG_CONFIG_PATH="$FAKEROOT/$OPENSSL/lib/pkgconfig:$FAKEROOT/$NGHTTP2/lib/pkgconfig" - -fetch "https://curl.haxx.se/download/$CURL.tar.gz" $CURL -default_build $CURL --enable-shared=no - -# Now we can build kore. -unset PKG_CONFIG -unset PKG_CONFIG_PATH - -export PATH=$FAKEROOT/bin:$PATH -export OPENSSL_PATH=$FAKEROOT/$OPENSSL - -cd $ROOT - -if [ ! -d kore ]; then - git clone https://git.kore.io/kore.git -fi - -pushd kore -make clean -LDFLAGS=-L$FAKEROOT/$NGHTTP2/lib make PYTHON=1 CURL=1 ACME=1 - -mv kore $ROOT/kore.bin -popd diff --git a/misc/kore-build/build-nghttp2.sh b/misc/kore-build/build-nghttp2.sh deleted file mode 100755 index 040a125..0000000 --- a/misc/kore-build/build-nghttp2.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh - -set -e - -. ./env.sh - -if [ $# -ne 1 ]; then - echo "Usage: build-nghttp2.sh [release]" - exit 1 -fi - -NAME=nghttp2-$1 - -fetch "https://github.com/nghttp2/nghttp2/releases/download/v$1/$NAME.tar.gz" $NAME - -default_build $NAME --enable-lib-only --prefix=$FAKEROOT/$NAME --enable-shared=no diff --git a/misc/kore-build/build-openssl.sh b/misc/kore-build/build-openssl.sh deleted file mode 100755 index 82f4093..0000000 --- a/misc/kore-build/build-openssl.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh - -set -e - -. ./env.sh - -if [ $# -ne 1 ]; then - echo "Usage: build-openssl.sh [release]" - exit 1 -fi - -NAME=openssl-$1 - -fetch "https://www.openssl.org/source/$NAME.tar.gz" $NAME - -build $NAME ./config no-shared --prefix=$FAKEROOT/$NAME diff --git a/misc/kore-build/build-python.sh b/misc/kore-build/build-python.sh deleted file mode 100755 index e2fb1af..0000000 --- a/misc/kore-build/build-python.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh - -set -e -set -x - -. ./env.sh - -if [ $# -ne 1 ]; then - echo "Usage: build-python.sh [release]" - exit 1 -fi - -NAME=Python-$1 - -fetch "https://www.python.org/ftp/python/$1/$NAME.tgz" $NAME - -default_build $NAME diff --git a/misc/kore-build/helpers.sh b/misc/kore-build/helpers.sh deleted file mode 100644 index c424908..0000000 --- a/misc/kore-build/helpers.sh +++ /dev/null @@ -1,72 +0,0 @@ -if [ -z "$ROOT" ]; then - echo "No ROOT set" - exit 1 -fi - -SOURCES=sources -BUILD=$ROOT/build -FAKEROOT=$ROOT/fakeroot - -LASTDIR=`pwd` - -pushd() { - LASTDIR=`pwd` - cd $1 -} - -popd() { - cd $LASTDIR -} - -fetch() { - url=$1 - name=$2 - - if [ -f $SOURCES/$name.tar.gz ]; then - return - fi - - curl -L "$url" > $SOURCES/$name.tar.gz -} - -default_build() { - name=$1 - shift - config=$* - - build $name ./configure --prefix=$FAKEROOT/$NAME $config -} - -build() { - name=$1 - shift - configcmd=$* - - if [ -f $BUILD/$name/.built ]; then - return - fi - - rm -rf $BUILD/$name - rm -rf $FAKEROOT/$name - - tar -zvxf $SOURCES/$name.tar.gz -C $BUILD - - pushd $BUILD/$name - mkdir -p $FAKEROOT/$name - - $configcmd - - make -j - make install - popd - - touch $BUILD/$name/.built -} - -setup() { - mkdir -p $BUILD - mkdir -p $SOURCES - mkdir -p $FAKEROOT -} - -setup diff --git a/misc/linux/aarch64_syscall.h.in b/misc/linux/aarch64_syscall.h.in index 955e2ca..47d1d51 100644 --- a/misc/linux/aarch64_syscall.h.in +++ b/misc/linux/aarch64_syscall.h.in @@ -287,4 +287,5 @@ #define __NR_fsconfig 431 #define __NR_fsmount 432 #define __NR_fspick 433 +#define __NR_clone3 435 diff --git a/misc/linux/arm_syscall.h.in b/misc/linux/arm_syscall.h.in index a565a4e..86334ed 100644 --- a/misc/linux/arm_syscall.h.in +++ b/misc/linux/arm_syscall.h.in @@ -387,6 +387,7 @@ #define __NR_fsconfig 431 #define __NR_fsmount 432 #define __NR_fspick 433 +#define __NR_clone3 435 #define __ARM_NR_breakpoint 0x0f0001 #define __ARM_NR_cacheflush 0x0f0002 diff --git a/misc/linux/x86_64_syscall.h.in b/misc/linux/x86_64_syscall.h.in index 2d4634f..d2cab06 100644 --- a/misc/linux/x86_64_syscall.h.in +++ b/misc/linux/x86_64_syscall.h.in @@ -343,4 +343,5 @@ #define __NR_fsconfig 431 #define __NR_fsmount 432 #define __NR_fspick 433 +#define __NR_clone3 435 diff --git a/misc/python3-config.sh b/misc/python3-config.sh index ee6cde7..c70bc58 100755 --- a/misc/python3-config.sh +++ b/misc/python3-config.sh @@ -5,10 +5,16 @@ if [ $# -ne 1 ]; then exit 1 fi -python3-config $1 --embed > /dev/null 2>&1 +if [ ! -z "$PYTHON_CONFIG" ]; then + BIN=$PYTHON_CONFIG +else + BIN=python3-config +fi + +$BIN $1 --embed > /dev/null 2>&1 if [ $? -eq 0 ]; then - python3-config $1 --embed + $BIN $1 --embed else - python3-config $1 + $BIN $1 fi diff --git a/share/man/kodev.1 b/share/man/kodev.1 deleted file mode 100644 index dcb6e70..0000000 --- a/share/man/kodev.1 +++ /dev/null @@ -1,278 +0,0 @@ -.TH KODEV 1 -.SH NAME -kodev \- Kore project management tool - -.SH SYNOPSIS -.BR kodev -[\fIOPTION\fR] ... - -.SH DESCRIPTION -This documentation describes the application management tools for building and -running a Kore project. For information regarding the technical interface and -C bindings, look to -.BR kore(3) -where these functions and structures are described. -.BR Kore -projects may be managed using the following OPTIONS; - -.BR create -.RS -Create a new application skeleton with the name that is passed to it. This will -create a new directory with all the files required to begin hacking. See the -\fBGENERATED FILES\fR section for more information. -.RE - -.BR build -.RS -Build the application. See the \fBBUILDING\fR section for more information. -.RE - -.BR run -.RS -Start the application in the foreground. See the \fBRUNNING\fR section for -more information. -.RE - -.BR reload -.RS -Reload the application. This is a shortcut to sending SIGHUP to the parent -process (see kore_pid). -.RE - -.BR info -.RS -Show information about the application configuration. Namely; active flavor, -output type, Kore features, Kore source and Kore binary. -.RE - -.BR clean -.RS -Cleanup the build files. -.RE - -.BR flavor -.RS -Switch between build flavors with the argument being the new flavor. -.RE - -.BR help -.RS -Show the help synopsis. -.RE - -.SH GENERATED FILES -Executing the -.BR create -command will generate several new files under the directory matching the -application name specified. - -These files are: - -.RS -.BR conf/build.conf -.RS -The build configuration. -.RE - -.BR conf/app.conf -.RS -The Kore application configuration. -.RE - -.BR src/app.c -.RS -The initial placeholder source code. -.RE - -.BR cert/server.pem -.RS -The self-signed auto-generated x509 certificate. -.RE - -.BR cert/key.pem -.RS -The key matching the self-signed x509 certificate. -.RE - -.BR dh2048.pem -.RS -The 2048-bit DH parameters used by TLS. -.RE -.RE - -Those files are: - -.RS -.BR kore.conf -.RS -The Kore application configuration. -.RE -.RE - -.RS -.BR handlers.py -.RS -The initial placeholder python page handler. -.RE -.RE - -.RS -.BR __init__.py -.RS -The python initialization code. Sets up the kore listener, etc. -.RE -.RE - -.RS -.BR cert/server.pem -.RS -The self-signed auto-generated x509 certificate. -.RE -.RE - -.RS -.BR cert/key.pem -.RS -The key matching the self-signed x509 certificate. -.RE -.RE - -.RS -.BR dh2048.pem -.RS -The 2048-bit DH parameters used by TLS. -.RE -.RE - - -.SH BUILDING -Executing the -.BR build -command will build your application. How this happens is instructed by -the -.BR conf/build.conf -configuration file. This file supports the following directives: - -.RS -.BR single_binary -[yes|no] -.RS -If set to \fByes\fR the build system will produce a single binary containing -both your application code and the Kore code allowing you to distribute -your application more easily. - -If set to \fBno\fR the build system will produce a standard dynamically -linked library that will be loaded into Kore at runtime. -.RE -.RE - -.RS -.BR kore_source -[path] -.RS -Must be set to point to the a Kore source code directory. Used only if -.BR single_binary -option is set to \fByes\fR. - -.BR Example: -kore_source=/home/joris/src/kore -.RE -.RE -.RE - -.RS -.BR kore_flavor -[build options] -.RS -Defines the build arguments for building Kore. Used only if -.BR single_binary -option is set to \fByes\fR. - -.BR Example: -kore_flavor=NOTLS=1 -.RE -.RE - -.RS -.BR cflags -.RS -Standard -.BR CFLAGS -used when compiling the application source code. -.RE -.RE - -.RS -.BR ldflags -.RS -Standard -.BR LDFLAGS -used when linking the application source code. -.RE -.RE - -Note that the -.BR build -command obeys the environment variables -.BR CC -and -.BR CXX - -.SH RUNNING -Executing the -.BR run -command will start your application in the foreground. - -What binary it executes depends -on whether or not the -.BR single_binary -flag was set in build configuration. If the -.BR single_binary -flag was enabled the -.BR run -command will execute the binary produced by the build system. If the -.BR single_binary -flag was not enabled the -.BR run -command will execute the -.BR $PREFIX/bin/kore -binary. -In both cases the -.BR run -command will pass the \fB\-fnr\fR command line options to the binary. -.RE - -.SH EXAMPLES -Changing flavor of the build; - -.RS -$ kodev flavor osx -.RE - -Building your application; - -.RS -$ kodev build -.RE - -.SH REPORTING BUGS, CONTRIBUTING && MORE -If you run into any bugs, have suggestions or patches, please contact me at -.BR - -More information can be found at -.BR - -.SH AUTHOR -.BR Kore -developed by Joris Vink -.BR - -Manpage authored by Guy Nankivell -.BR - -.SH LICENCE -Usage of this software is provided under the -.BR ISC -license which may be found, with the source, at -.BR - diff --git a/src/bsd.c b/src/bsd.c index 8799504..2505694 100644 --- a/src/bsd.c +++ b/src/bsd.c @@ -298,6 +298,12 @@ kore_platform_sandbox(void) #endif } +u_int32_t +kore_platform_random_uint32(void) +{ + return (arc4random()); +} + #if defined(KORE_USE_PLATFORM_PLEDGE) void kore_platform_pledge(void) diff --git a/src/cli.c b/src/cli.c index 5ea164e..59a466e 100644 --- a/src/cli.c +++ b/src/cli.c @@ -43,6 +43,24 @@ #include #include +/* + * Turn off deprecated function warnings when building against OpenSSL 3. + * + * The OpenSSL 3 library deprecated most low-level functions in favour + * for their higher level APIs. + * + * I am planning a replacement, but for now we can still make it build + * and function by ignoring these warnings completely. + * + * The functions in question are: + * - SHA256_Init, SHA256_Update, SHA256_Final + * - RSA_new, RSA_generate_key_ex + * - EVP_PKEY_assign + */ +#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3 +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + #define errno_s strerror(errno) #define ssl_errno_s ERR_error_string(ERR_get_error(), NULL) @@ -54,6 +72,8 @@ #define BUILD_C 1 #define BUILD_CXX 2 +#define CLANGDB_FILE_PATH "compile_commands.json" + struct cli_buf { u_int8_t *data; size_t length; @@ -120,6 +140,8 @@ static char *cli_text_trim(char *, size_t); static char *cli_read_line(FILE *, char *, size_t); static long long cli_strtonum(const char *, long long, long long); static int cli_split_string(char *, const char *, char **, size_t); +static int cli_generate_compiler_args(struct cfile *, char **, + char **, size_t); static void usage(void) __attribute__((noreturn)); static void fatal(const char *, ...) __attribute__((noreturn)) @@ -186,6 +208,7 @@ static void cli_run(int, char **); static void cli_help(int, char **); static void cli_info(int, char **); static void cli_build(int, char **); +static void cli_build_help(void); static void cli_clean(int, char **); static void cli_source(int, char **); static void cli_reload(int, char **); @@ -194,6 +217,7 @@ static void cli_cflags(int, char **); static void cli_ldflags(int, char **); static void cli_genasset(int, char **); static void cli_genasset_help(void); +static void cli_build_clangdb(const char *); #if !defined(KODEV_MINIMAL) static void cli_create(int, char **); @@ -427,10 +451,6 @@ main(int argc, char **argv) for (i = 0; cmds[i].name != NULL; i++) { if (!strcmp(argv[0], cmds[i].name)) { - if (strcmp(argv[0], "create")) { - argc--; - argv++; - } command = &cmds[i]; cmds[i].cb(argc, argv); break; @@ -545,7 +565,7 @@ cli_flavor(int argc, char **argv) (void)cli_buildopt_new("_default"); cli_buildopt_parse("conf/build.conf"); - if (argc == 0) { + if (argc < 2) { cli_flavor_load(); TAILQ_FOREACH(bopt, &build_options, list) { if (!strcmp(bopt->name, "_default")) @@ -557,13 +577,91 @@ cli_flavor(int argc, char **argv) } } } else { - cli_flavor_change(argv[0]); - printf("changed build flavor to: %s\n", argv[0]); + cli_flavor_change(argv[1]); + printf("changed build flavor to: %s\n", argv[1]); } cli_buildopt_cleanup(); } +static void +cli_build_clangdb(const char *pwd) +{ + struct cfile *cf; + int fd, i, nargs, genpath_len; + char *args[64 + CFLAGS_MAX], *genpath, *ext; + + printf("generating %s...\n", CLANGDB_FILE_PATH); + + genpath_len = cli_vasprintf(&genpath, "%s/", object_dir); + + cli_file_open(CLANGDB_FILE_PATH, O_CREAT | O_TRUNC | O_WRONLY, &fd); + cli_file_writef(fd, "[\n"); + + TAILQ_FOREACH(cf, &source_files, list) { + int tempbuild = cf->build; + + /* Exclude generated source files. */ + if (!strncmp(cf->fpath, genpath, genpath_len)) + continue; + + if (cf->build == BUILD_NOBUILD) { + if ((ext = strrchr(cf->fpath, '.')) == NULL) + continue; + + /* + * Temporarily rewrite build to our file type to + * include unchanged files. + */ + if (!strcmp(ext, ".cpp")) + cf->build = BUILD_CXX; + else if (!strcmp(ext, ".c")) + cf->build = BUILD_C; + else + continue; + } + + cli_file_writef(fd, "\t{\n"); + cli_file_writef(fd, "\t\t\"arguments\": [\n"); + + nargs = cli_generate_compiler_args(cf, NULL, args, + 64 + CFLAGS_MAX); + + for (i = 0; i < nargs; i++) { + cli_file_writef(fd, "\t\t\t\"%s\"%s\n", + args[i], i == nargs - 1 ? "" : ","); + } + + cli_file_writef(fd, "\t\t],\n"); + cli_file_writef(fd, "\t\t\"directory\": \"%s\",\n", pwd); + cli_file_writef(fd, "\t\t\"file\": \"%s\"\n", cf->fpath); + cli_file_writef(fd, "\t}%s\n", + cf == TAILQ_LAST(&source_files, cfile_list) ? "" : ","); + + cf->build = tempbuild; + } + + cli_file_writef(fd, "]\n"); + cli_file_close(fd); + + free(genpath); + + printf("%s generated successfully...\n", CLANGDB_FILE_PATH); +} + +static void +cli_build_help(void) +{ + printf("Usage: kodev build [-c]\n"); + printf("Synopsis:\n"); + printf(" Build a kore application in current working directory.\n"); + printf("\n"); + printf(" Optional flags:\n"); + printf("\t-c = generate Clang compilation database after build\n"); + + exit(1); +} + static void cli_build(int argc, char **argv) { @@ -581,6 +679,23 @@ cli_build(int argc, char **argv) char *sofile, *config; char *assets_path, *p, *src_path; char pwd[PATH_MAX], *assets_header; + int ch, clangdb; + + clangdb = 0; + + while ((ch = getopt(argc, argv, "ch")) != -1) { + switch (ch) { + case 'h': + cli_build_help(); + break; + case 'c': + clangdb = 1; + break; + default: + cli_build_help(); + break; + } + } if (getcwd(pwd, sizeof(pwd)) == NULL) fatal("could not get cwd: %s", errno_s); @@ -733,6 +848,9 @@ cli_build(int argc, char **argv) printf("nothing to be done!\n"); } + if (clangdb) + cli_build_clangdb(pwd); + if (run_after == 0) cli_buildopt_cleanup(); } @@ -891,7 +1009,7 @@ cli_genasset(int argc, char **argv) if (getenv("KORE_OBJDIR") == NULL) object_dir = out_dir; - if (argv[0] == NULL) + if (argv[1] == NULL) cli_genasset_help(); (void)cli_vasprintf(&hdr, "%s/assets.h", out_dir); @@ -901,20 +1019,20 @@ cli_genasset(int argc, char **argv) cli_file_writef(s_fd, "#ifndef __H_KORE_ASSETS_H\n"); cli_file_writef(s_fd, "#define __H_KORE_ASSETS_H\n"); - if (stat(argv[0], &st) == -1) - fatal("%s: %s", argv[0], errno_s); + if (stat(argv[1], &st) == -1) + fatal("%s: %s", argv[1], errno_s); if (S_ISDIR(st.st_mode)) { - if (cli_dir_exists(argv[0])) - cli_find_files(argv[0], cli_build_asset); + if (cli_dir_exists(argv[1])) + cli_find_files(argv[1], cli_build_asset); } else if (S_ISREG(st.st_mode)) { memset(&dp, 0, sizeof(dp)); dp.d_type = DT_REG; (void)snprintf(dp.d_name, sizeof(dp.d_name), "%s", - basename(argv[0])); - cli_build_asset(argv[0], &dp); + basename(argv[1])); + cli_build_asset(argv[1], &dp); } else { - fatal("%s is not a directory or regular file", argv[0]); + fatal("%s is not a directory or regular file", argv[1]); } cli_file_writef(s_fd, "\n#endif\n"); @@ -1540,35 +1658,35 @@ cli_generate_certs(void) } #endif -static void -cli_compile_source_file(void *arg) +static int +cli_generate_compiler_args(struct cfile *cf, char **cout, + char **args, size_t elm) { - struct cfile *cf; - int idx, i; - char **flags; - char *compiler; - int flags_count; - char *args[34 + CFLAGS_MAX]; - - cf = arg; + char *compiler, **flags; + int idx, i, flags_count; switch (cf->build) { case BUILD_C: - compiler = compiler_c; flags = cflags; + compiler = compiler_c; flags_count = cflags_count; break; case BUILD_CXX: - compiler = compiler_cpp; flags = cxxflags; + compiler = compiler_cpp; flags_count = cxxflags_count; break; default: - fatal("cli_compile_file: unexpected file type: %d", - cf->build); - break; + fatal("%s: unexpected file type: %d", __func__, cf->build); + /* NOTREACHED */ } + if ((size_t)flags_count + 2 >= elm) + fatal("%s: flags %d >= %zu", __func__, flags_count, elm); + + if (cout != NULL) + *cout = compiler; + idx = 0; args[idx++] = compiler; @@ -1583,6 +1701,17 @@ cli_compile_source_file(void *arg) args[idx++] = cf->opath; args[idx] = NULL; + return (idx); +} + +static void +cli_compile_source_file(void *arg) +{ + char *compiler; + char *args[64 + CFLAGS_MAX]; + + cli_generate_compiler_args(arg, &compiler, args, 64 + CFLAGS_MAX); + execvp(compiler, args); fatal("failed to start '%s': %s", compiler, errno_s); } diff --git a/src/config.c b/src/config.c index 01946fa..3bc2109 100644 --- a/src/config.c +++ b/src/config.c @@ -93,6 +93,7 @@ static int configure_socket_backlog(char *); static int configure_privsep_skip(char *); static int configure_privsep_root(char *); static int configure_privsep_runas(char *); +static int configure_deployment(char *); #if defined(KORE_USE_PLATFORM_PLEDGE) static int configure_add_pledge(char *); @@ -157,7 +158,6 @@ static int configure_task_threads(char *); #endif #if defined(KORE_USE_PYTHON) -static int configure_deployment(char *); static int configure_python_path(char *); static int configure_python_import(char *); #endif @@ -240,6 +240,7 @@ static struct { { "tls_cipher", configure_tls_cipher }, { "tls_dhparam", configure_tls_dhparam }, { "rand_file", configure_rand_file }, + { "deployment", configure_deployment }, #if defined(KORE_USE_ACME) { "acme_email", configure_acme_email }, { "acme_provider", configure_acme_provider }, @@ -269,9 +270,6 @@ static struct { { "websocket_maxframe", configure_websocket_maxframe }, { "websocket_timeout", configure_websocket_timeout }, #endif -#if defined(KORE_USE_PYTHON) - { "deployment", configure_deployment }, -#endif #if defined(KORE_USE_PGSQL) { "pgsql_conn_max", configure_pgsql_conn_max }, { "pgsql_queue_limit", configure_pgsql_queue_limit }, @@ -524,7 +522,6 @@ kore_parse_config_file(FILE *fp) } } -#if defined(KORE_USE_PYTHON) int kore_configure_setting(const char *name, char *value) { @@ -544,7 +541,6 @@ kore_configure_setting(const char *name, char *value) kore_log(LOG_NOTICE, "ignoring unknown kore.config.%s setting", name); return (KORE_RESULT_OK); } -#endif static void configure_check_var(char **var, const char *other, const char *logmsg) @@ -2015,7 +2011,6 @@ configure_task_threads(char *option) } #endif -#if defined(KORE_USE_PYTHON) static int configure_deployment(char *value) { @@ -2040,6 +2035,7 @@ configure_deployment(char *value) return (KORE_RESULT_OK); } +#if defined(KORE_USE_PYTHON) static int configure_python_path(char *path) { diff --git a/src/connection.c b/src/connection.c index 55685b0..57580d7 100644 --- a/src/connection.c +++ b/src/connection.c @@ -399,3 +399,52 @@ kore_connection_nonblock(int fd, int nodelay) return (KORE_RESULT_OK); } + +void +kore_connection_log(struct connection *c, const char *fmt, ...) +{ + struct kore_buf buf; + va_list args; + char *ptr; + + kore_buf_init(&buf, 128); + kore_buf_appendf(&buf, "ip=[%s] msg=[", kore_connection_ip(c)); + + va_start(args, fmt); + kore_buf_appendv(&buf, fmt, args); + va_end(args); + + kore_buf_appendf(&buf, "]"); + + ptr = kore_buf_stringify(&buf, NULL); + kore_log(LOG_NOTICE, "%s", ptr); + kore_free(ptr); +} + +const char * +kore_connection_ip(struct connection *c) +{ + static char addr[INET6_ADDRSTRLEN]; + + memset(addr, 0, sizeof(addr)); + + switch (c->family) { + case AF_INET: + if (inet_ntop(c->family, + &(c->addr.ipv4.sin_addr), addr, sizeof(addr)) == NULL) + fatal("inet_ntop: %s", errno_s); + break; + case AF_INET6: + if (inet_ntop(c->family, + &(c->addr.ipv6.sin6_addr), addr, sizeof(addr)) == NULL) + fatal("inet_ntop: %s", errno_s); + break; + case AF_UNIX: + (void)kore_strlcpy(addr, "unix-socket", sizeof(addr)); + break; + default: + fatal("unknown family %d", c->family); + } + + return (addr); +} diff --git a/src/filemap.c b/src/filemap.c index ddb9d48..15fb8d2 100644 --- a/src/filemap.c +++ b/src/filemap.c @@ -195,7 +195,7 @@ filemap_serve(struct http_request *req, const struct filemap_entry *map) return; } - if (!http_argument_urldecode(fpath)) { + if (!http_argument_urldecode(fpath, 1)) { http_response(req, HTTP_STATUS_BAD_REQUEST, NULL, 0); return; } diff --git a/src/http.c b/src/http.c index 28f3116..626bf06 100644 --- a/src/http.c +++ b/src/http.c @@ -799,7 +799,7 @@ http_header_recv(struct netbuf *nb) c = nb->owner; - if (nb->b_len < 4) + if (nb->s_off < 4) return (KORE_RESULT_OK); if (!isalpha(nb->buf[0])) { @@ -811,8 +811,14 @@ http_header_recv(struct netbuf *nb) end_headers = kore_mem_find(nb->buf, nb->s_off, "\r\n\r\n", 4); if (end_headers == NULL) { end_headers = kore_mem_find(nb->buf, nb->s_off, "\n\n", 2); - if (end_headers == NULL) + if (end_headers == NULL) { + if (nb->s_off == http_header_max) { + http_error_response(c, + HTTP_STATUS_REQUEST_ENTITY_TOO_LARGE); + return (KORE_RESULT_ERROR); + } return (KORE_RESULT_OK); + } skip = 2; } @@ -919,6 +925,7 @@ http_header_recv(struct netbuf *nb) } if (req->content_length == 0) { + c->http_timeout = 0; req->flags |= HTTP_REQUEST_COMPLETE; req->flags &= ~HTTP_REQUEST_EXPECT_BODY; return (KORE_RESULT_OK); @@ -1001,7 +1008,7 @@ http_argument_get(struct http_request *req, const char *name, } int -http_argument_urldecode(char *arg) +http_argument_urldecode(char *arg, int url) { u_int8_t v; int err; @@ -1039,8 +1046,14 @@ http_argument_urldecode(char *arg) if (err != KORE_RESULT_OK) return (err); - if (v <= 0x1f || v == 0x7f) - return (KORE_RESULT_ERROR); + if (url) { + if (v <= 0x1f || v == 0x7f) + return (KORE_RESULT_ERROR); + } else { + if ((v <= 0x1f || v == 0x7f) && + (v != '\n' && v != '\r')) + return (KORE_RESULT_ERROR); + } *in++ = (char)v; p += 3; @@ -1791,6 +1804,9 @@ http_validate_header(char *header) break; } + if (*p >= 'A' && *p <= 'Z') + *p += 32; + if (http_token[idx] == 0x00) return (NULL); } @@ -2274,7 +2290,7 @@ http_argument_add(struct http_request *req, char *name, char *value, int qs, struct kore_route_params *p; if (decode) { - if (!http_argument_urldecode(name)) + if (!http_argument_urldecode(name, qs)) return; } @@ -2291,7 +2307,7 @@ http_argument_add(struct http_request *req, char *name, char *value, int qs, continue; if (decode) { - if (!http_argument_urldecode(value)) + if (!http_argument_urldecode(value, qs)) return; } @@ -2342,6 +2358,7 @@ http_body_update(struct http_request *req, const void *data, size_t len) req->content_length -= len; if (req->content_length == 0) { + req->owner->http_timeout = 0; req->owner->rnb->extra = NULL; http_request_wakeup(req); req->flags |= HTTP_REQUEST_COMPLETE; diff --git a/src/keymgr_openssl.c b/src/keymgr_openssl.c index 98ffbbc..d9c4084 100644 --- a/src/keymgr_openssl.c +++ b/src/keymgr_openssl.c @@ -63,6 +63,16 @@ #include "acme.h" #endif +/* + * Disable deprecated declaration warnings if we're building against + * OpenSSL 3 as they marked all low-level APIs as deprecated. + * + * Work is being done to replace these, but for now let things build. + */ +#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3 +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + #define RAND_TMP_FILE "rnd.tmp" #define RAND_POLL_INTERVAL (1800 * 1000) #define RAND_FILE_SIZE 1024 @@ -168,10 +178,11 @@ struct key { TAILQ_ENTRY(key) list; }; -char *kore_rand_file = NULL; - -static TAILQ_HEAD(, key) keys; -static int initialized = 0; +/* Helper for weird API designs (looking at you OpenSSL). */ +union deconst { + void *p; + const void *cp; +}; #if defined(KORE_USE_ACME) @@ -251,8 +262,6 @@ static void keymgr_x509_msg(const char *, const void *, size_t, int, int); static void keymgr_rsa_encrypt(struct kore_msg *, const void *, struct key *); -static void keymgr_ecdsa_sign(struct kore_msg *, const void *, - struct key *); #if defined(__OpenBSD__) #if defined(KORE_USE_ACME) @@ -262,6 +271,11 @@ static const char *keymgr_pledges = "stdio rpath"; #endif #endif +static TAILQ_HEAD(, key) keys; +static int initialized = 0; + +char *kore_rand_file = NULL; + void kore_keymgr_run(void) { @@ -658,9 +672,6 @@ keymgr_msg_recv(struct kore_msg *msg, const void *data) case EVP_PKEY_RSA: keymgr_rsa_encrypt(msg, data, key); break; - case EVP_PKEY_EC: - keymgr_ecdsa_sign(msg, data, key); - break; default: break; } @@ -685,6 +696,7 @@ keymgr_msg_recv(struct kore_msg *msg, const void *data) static void keymgr_rsa_encrypt(struct kore_msg *msg, const void *data, struct key *key) { + union deconst cp; int ret; RSA *rsa; const struct kore_keyreq *req; @@ -692,7 +704,9 @@ keymgr_rsa_encrypt(struct kore_msg *msg, const void *data, struct key *key) u_int8_t buf[1024]; req = (const struct kore_keyreq *)data; - rsa = EVP_PKEY_get0_RSA(key->pkey); + cp.cp = EVP_PKEY_get0_RSA(key->pkey); + + rsa = cp.p; keylen = RSA_size(rsa); if (req->data_len > keylen || keylen > sizeof(buf)) @@ -706,32 +720,6 @@ keymgr_rsa_encrypt(struct kore_msg *msg, const void *data, struct key *key) kore_msg_send(msg->src, KORE_MSG_KEYMGR_RESP, buf, ret); } -static void -keymgr_ecdsa_sign(struct kore_msg *msg, const void *data, struct key *key) -{ - size_t len; - EC_KEY *ec; - const struct kore_keyreq *req; - unsigned int siglen; - u_int8_t sig[1024]; - - req = (const struct kore_keyreq *)data; - ec = EVP_PKEY_get0_EC_KEY(key->pkey); - - len = ECDSA_size(ec); - if (req->data_len > len || len > sizeof(sig)) - return; - - if (ECDSA_sign(EVP_PKEY_NONE, req->data, req->data_len, - sig, &siglen, ec) == 0) - return; - - if (siglen > sizeof(sig)) - return; - - kore_msg_send(msg->src, KORE_MSG_KEYMGR_RESP, sig, siglen); -} - static void keymgr_x509_msg(const char *domain, const void *data, size_t len, int target, int msg) @@ -759,7 +747,7 @@ keymgr_x509_msg(const char *domain, const void *data, size_t len, static void keymgr_acme_init(void) { - RSA *rsa; + const RSA *rsa; struct key *key; char *e, *n; int needsreg; diff --git a/src/kore.c b/src/kore.c index 9a2a05f..5396c5e 100644 --- a/src/kore.c +++ b/src/kore.c @@ -46,6 +46,10 @@ #include "python_api.h" #endif +#if defined(KORE_USE_LUA) +#include "lua_api.h" +#endif + #if defined(KORE_USE_ACME) #include "acme.h" #endif @@ -61,7 +65,8 @@ int skip_runas = 0; int skip_chroot = 0; u_int8_t worker_count = 0; char **kore_argv = NULL; -int kore_foreground = 0; +int kore_mem_guard = 0; +int kore_foreground = 1; char *kore_progname = NULL; u_int32_t kore_socket_backlog = 5000; int kore_quit = KORE_QUIT_NONE; @@ -82,42 +87,51 @@ static void kore_server_shutdown(void); static void kore_server_start(int, char *[]); static void kore_call_parent_configure(int, char **); -#if !defined(KORE_SINGLE_BINARY) && defined(KORE_USE_PYTHON) -static const char *parent_config_hook = KORE_PYTHON_CONFIG_HOOK; -static const char *parent_teardown_hook = KORE_PYTHON_TEARDOWN_HOOK; -#else +#if !defined(KORE_SINGLE_BINARY) +static const char *rarg0 = NULL; +#endif + static const char *parent_config_hook = KORE_CONFIG_HOOK; static const char *parent_teardown_hook = KORE_TEARDOWN_HOOK; + #if defined(KORE_SINGLE_BINARY) static const char *parent_daemonized_hook = KORE_DAEMONIZED_HOOK; #endif -#endif static void usage(void) { -#if defined(KORE_USE_PYTHON) - printf("Usage: %s [options] [app | app.py]\n", __progname); -#else - printf("Usage: %s [options]\n", __progname); -#endif + if (kore_runtime_count() > 0) { + printf("Usage: %s [options] [app | script]\n", __progname); + } else { + printf("Usage: %s [options]\n", __progname); + } printf("\n"); - printf("Available options:\n"); + printf("Command-line options:\n"); #if !defined(KORE_SINGLE_BINARY) - printf("\t-c\tconfiguration to use\n"); + printf("\t-c\tThe configuration file to load when starting.\n"); #endif -#if defined(KORE_DEBUG) - printf("\t-d\trun with debug on\n"); -#endif - printf("\t-f\tstart in foreground\n"); - printf("\t-h\tthis help text\n"); - printf("\t-n\tdo not chroot on any worker\n"); - printf("\t-q\tonly log errors\n"); - printf("\t-r\tdo not change user on any worker\n"); - printf("\t-v\tdisplay %s build information\n", __progname); + printf("\t-f\tDo not daemonize, everything runs in the foreground.\n"); + printf("\t-h\tThis help text.\n"); + printf("\t-n\tDo not do the chroot privsep step.\n"); + printf("\t-q\tQuiet mode, only logs errors.\n"); + printf("\t-r\tDo not do the privsep user swapping step.\n"); + printf("\t-v\tDisplay %s build information.\n", __progname); - printf("\nFind more information on https://kore.io\n"); + printf("\n"); + printf("Environment options:\n"); + printf(" env KORE_MEM_GUARD=1\n"); + printf(" Enables memory pool guards and other protections.\n"); + printf("\n"); + printf(" Enabling this will include guard pages for each\n"); + printf(" pool entry allocations and mark pool entries as\n"); + printf(" PROT_NONE when unused.\n"); + printf("\n"); + printf(" This catches bugs and prevents memory vulnerabilities\n"); + printf(" but with performance and memory pressure costs.\n"); + + printf("\n"); exit(1); } @@ -138,17 +152,22 @@ version(void) #if defined(KORE_USE_TASKS) printf("tasks "); #endif -#if defined(KORE_DEBUG) - printf("debug "); -#endif #if defined(KORE_USE_PYTHON) printf("python-%s ", PY_VERSION); #endif +#if defined(KORE_USE_LUA) + printf("lua-%s.%s.%s ", + LUA_VERSION_MAJOR, LUA_VERSION_MINOR, LUA_VERSION_RELEASE); +#endif #if defined(KORE_USE_ACME) printf("acme "); +#endif +#if defined(KORE_DEBUG) + printf("debug "); #endif if (!kore_tls_supported()) printf("notls "); + printf("\n"); exit(0); } @@ -156,10 +175,10 @@ version(void) int main(int argc, char *argv[]) { - struct kore_runtime_call *rcall; -#if !defined(KORE_SINGLE_BINARY) && defined(KORE_USE_PYTHON) +#if !defined(KORE_SINGLE_BINARY) struct stat st; #endif + struct kore_runtime_call *rcall; kore_argc = argc; kore_argv = argv; @@ -171,6 +190,7 @@ main(int argc, char *argv[]) kore_mem_init(); kore_msg_init(); kore_log_init(); + kore_tls_init(); kore_progname = kore_strdup(argv[0]); kore_proctitle_setup(); @@ -180,23 +200,22 @@ main(int argc, char *argv[]) argv += optind; #endif -#if !defined(KORE_SINGLE_BINARY) && defined(KORE_USE_PYTHON) - if (argc > 0) { - kore_pymodule = argv[0]; - argc--; - argv++; - } else { - kore_pymodule = NULL; - } - - if (kore_pymodule) { - if (lstat(kore_pymodule, &st) == -1) { - fatal("failed to stat '%s': %s", - kore_pymodule, errno_s); +#if !defined(KORE_SINGLE_BINARY) + if (kore_runtime_count() > 0) { + if (argc > 0) { + rarg0 = argv[0]; + argc--; + argv++; } - if (!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode)) - fatal("%s: not a directory or file", kore_pymodule); + if (rarg0) { + if (lstat(rarg0, &st) == -1) { + if (errno == ENOENT) + rarg0 = NULL; + else + fatal("stat(%s): %s", rarg0, errno_s); + } + } } #endif @@ -222,35 +241,30 @@ main(int argc, char *argv[]) #endif kore_domain_init(); kore_module_init(); - kore_tls_init(); -#if !defined(KORE_SINGLE_BINARY) && !defined(KORE_USE_PYTHON) - if (config_file == NULL) +#if !defined(KORE_SINGLE_BINARY) + if (kore_runtime_count() == 0 && config_file == NULL) usage(); #endif kore_module_load(NULL, NULL, KORE_MODULE_NATIVE); #if defined(KORE_USE_PYTHON) kore_python_init(); -#if !defined(KORE_SINGLE_BINARY) - if (kore_pymodule) { - kore_module_load(kore_pymodule, NULL, KORE_MODULE_PYTHON); - if (S_ISDIR(st.st_mode) && chdir(kore_pymodule) == -1) - fatal("chdir(%s): %s", kore_pymodule, errno_s); - } else { - /* swap back to non-python hooks. */ - parent_config_hook = KORE_CONFIG_HOOK; - parent_teardown_hook = KORE_TEARDOWN_HOOK; - } #endif + +#if defined(KORE_USE_LUA) + kore_lua_init(); +#endif + +#if !defined(KORE_SINGLE_BINARY) + if (kore_runtime_count() > 0 && rarg0 != NULL) + kore_runtime_resolve(rarg0, &st); #endif #if defined(KORE_SINGLE_BINARY) kore_call_parent_configure(argc, argv); -#endif - -#if defined(KORE_USE_PYTHON) && !defined(KORE_SINGLE_BINARY) - if (kore_pymodule) +#else + if (kore_runtime_count() > 0 && rarg0 != NULL) kore_call_parent_configure(argc, argv); #endif @@ -292,6 +306,10 @@ main(int argc, char *argv[]) kore_python_cleanup(); #endif +#if defined(KORE_USE_LUA) + kore_lua_cleanup(); +#endif + kore_mem_cleanup(); return (kore_quit); @@ -303,9 +321,9 @@ kore_default_getopt(int argc, char **argv) int ch; #if !defined(KORE_SINGLE_BINARY) - while ((ch = getopt(argc, argv, "c:fhnqrv")) != -1) { + while ((ch = getopt(argc, argv, "c:dfhnqrv")) != -1) { #else - while ((ch = getopt(argc, argv, "fhnqrv")) != -1) { + while ((ch = getopt(argc, argv, "dfhnqrv")) != -1) { #endif switch (ch) { #if !defined(KORE_SINGLE_BINARY) @@ -315,8 +333,12 @@ kore_default_getopt(int argc, char **argv) fatal("strdup"); break; #endif + case 'd': + kore_foreground = 0; + break; case 'f': - kore_foreground = 1; + printf("note: -f is the default now, " + "use -d to daemonize\n"); break; case 'h': usage(); @@ -752,6 +774,17 @@ kore_proctitle(const char *title) memset(kore_argv[0] + len, 0, proctitle_maxlen - len); } +void +kore_hooks_set(const char *config, const char *teardown, const char *daemonized) +{ + parent_config_hook = config; + parent_teardown_hook = teardown; + +#if defined(KORE_SINGLE_BINARY) + parent_daemonized_hook = daemonized; +#endif +} + static void kore_proctitle_setup(void) { @@ -789,6 +822,8 @@ kore_server_start(int argc, char *argv[]) if (!kore_quiet) { kore_log(LOG_INFO, "%s %s starting, built=%s", __progname, kore_version, kore_build_date); + kore_log(LOG_INFO, "memory pool protections: %s", + kore_mem_guard ? "enabled" : "disabled"); kore_log(LOG_INFO, "built-ins: " #if defined(__linux__) "seccomp " @@ -810,10 +845,15 @@ kore_server_start(int argc, char *argv[]) #endif #if defined(KORE_USE_CURL) "curl " +#endif +#if defined(KORE_USE_LUA) + "lua " #endif ); } + kore_tls_log_version(); + if (kore_foreground == 0) { if (daemon(1, 0) == -1) fatal("cannot daemon(): %s", errno_s); @@ -829,12 +869,8 @@ kore_server_start(int argc, char *argv[]) kore_pid = getpid(); kore_write_kore_pid(); -#if !defined(KORE_SINGLE_BINARY) && !defined(KORE_USE_PYTHON) - kore_call_parent_configure(argc, argv); -#endif - -#if defined(KORE_USE_PYTHON) && !defined(KORE_SINGLE_BINARY) - if (kore_pymodule == NULL) +#if !defined(KORE_SINGLE_BINARY) + if (kore_runtime_count() == 0 || rarg0 == NULL) kore_call_parent_configure(argc, argv); #endif diff --git a/src/linux.c b/src/linux.c index e9a9894..8c6e82d 100644 --- a/src/linux.c +++ b/src/linux.c @@ -15,6 +15,7 @@ */ #include +#include #include #include #include @@ -59,7 +60,7 @@ kore_platform_worker_setcpu(struct kore_worker *kw) CPU_SET(kw->cpu, &cpuset); if (sched_setaffinity(0, sizeof(cpu_set_t), &cpuset) == -1) - kore_log(LOG_WARNING, "kore_worker_setcpu(): %s", errno_s); + kore_log(LOG_NOTICE, "kore_worker_setcpu(): %s", errno_s); } void @@ -262,3 +263,18 @@ kore_platform_sandbox(void) { kore_seccomp_enable(); } + +u_int32_t +kore_platform_random_uint32(void) +{ + ssize_t ret; + u_int32_t val; + + if ((ret = getrandom(&val, sizeof(val), 0)) == -1) + fatalx("getrandom(): %s", errno_s); + + if ((size_t)ret != sizeof(val)) + fatalx("getrandom() %zd != %zu", ret, sizeof(val)); + + return (val); +} diff --git a/src/log.c b/src/log.c index 4255b72..fb51ada 100644 --- a/src/log.c +++ b/src/log.c @@ -95,9 +95,9 @@ kore_log(int prio, const char *fmt, ...) str = kore_buf_stringify(&buf, NULL); if (kore_foreground || fp != stdout) - log_print(prio, "[parent]: %s\n", str); + log_print(prio, "proc=[parent] log=[%s]\n", str); else - syslog(prio, "[parent]: %s", str); + syslog(prio, "proc=[parent] log=[%s]", str); } kore_buf_cleanup(&buf); @@ -120,10 +120,10 @@ log_from_worker(struct kore_msg *msg, const void *data) name = kore_worker_name(wlog->wid); if (kore_foreground || fp != stdout) { - log_print(wlog->prio, "%s: %.*s\n", + log_print(wlog->prio, "proc=%s log=[%.*s]\n", name, (int)wlog->loglen, wlog->logmsg); } else { - syslog(wlog->prio, "%s: %.*s", + syslog(wlog->prio, "proc=%s log=[%.*s]", name, (int)wlog->loglen, wlog->logmsg); } } diff --git a/src/lua.c b/src/lua.c new file mode 100644 index 0000000..cc1eac1 --- /dev/null +++ b/src/lua.c @@ -0,0 +1,601 @@ +/* + * Copyright (c) 2023 Joris Vink + * + * 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 + +#include "kore.h" + +#if !defined(KORE_NO_HTTP) +#include "http.h" +#endif + +#include "lua_api.h" +#include "lua_methods.h" + +struct lua_http_request { + struct http_request *req; +}; + +struct lua_symbol { + lua_State *L; + int ref; + LIST_ENTRY(lua_symbol) list; +}; + +struct lua_module { + lua_State *L; + LIST_HEAD(, lua_symbol) symbols; +}; + +static int lua_runtime_resolve(const char *, const struct stat *); +static int lua_runtime_http_request(void *, struct http_request *); +static void lua_runtime_http_request_free(void *, struct http_request *); +static void lua_runtime_http_body_chunk(void *, struct http_request *, + const void *, size_t); +static int lua_runtime_validator(void *, struct http_request *, + const void *); +static void lua_runtime_wsmessage(void *, struct connection *, + u_int8_t, const void *, size_t); +static void lua_runtime_execute(void *); +static int lua_runtime_onload(void *, int); +static void lua_runtime_signal(void *, int); +static void lua_runtime_configure(void *, int, char **); +static void lua_runtime_connect(void *, struct connection *); + +static void lua_module_load(struct kore_module *); +static void lua_module_free(struct kore_module *); +static void lua_module_reload(struct kore_module *); +static void *lua_module_getsym(struct kore_module *, const char *); + +static void *lua_mem_alloc(void *, void *, size_t, size_t); + +static int lua_kore_module_init(lua_State *); +static void lua_symbol_resolve(struct lua_symbol *, lua_State **); + +static int lua_argument_get_bool(lua_State *, const char *); +static const char *lua_argument_get_string(lua_State *, const char *); + +struct kore_module_functions kore_lua_module = { + .free = lua_module_free, + .load = lua_module_load, + .getsym = lua_module_getsym, + .reload = lua_module_reload +}; + +struct kore_runtime kore_lua_runtime = { + KORE_RUNTIME_LUA, + .resolve = lua_runtime_resolve, + .http_request = lua_runtime_http_request, + .http_body_chunk = lua_runtime_http_body_chunk, + .http_request_free = lua_runtime_http_request_free, + .validator = lua_runtime_validator, + .wsconnect = lua_runtime_connect, + .wsmessage = lua_runtime_wsmessage, + .wsdisconnect = lua_runtime_connect, + .onload = lua_runtime_onload, + .signal = lua_runtime_signal, + .connect = lua_runtime_connect, + .execute = lua_runtime_execute, + .configure = lua_runtime_configure, +}; + +#define LUA_CONSTANT(x) { #x, x } + +static struct { + const char *symbol; + int value; +} lua_integers[] = { + LUA_CONSTANT(LOG_ERR), + LUA_CONSTANT(LOG_INFO), + LUA_CONSTANT(LOG_NOTICE), + LUA_CONSTANT(HTTP_METHOD_GET), + LUA_CONSTANT(HTTP_METHOD_PUT), + LUA_CONSTANT(HTTP_METHOD_POST), + LUA_CONSTANT(HTTP_METHOD_HEAD), + LUA_CONSTANT(HTTP_METHOD_PATCH), + LUA_CONSTANT(HTTP_METHOD_DELETE), + LUA_CONSTANT(HTTP_METHOD_OPTIONS), + { NULL, -1 }, +}; + +void +kore_lua_init(void) +{ + if (!kore_configure_setting("deployment", "dev")) + fatal("failed to set initial deployment to dev"); +} + +void +kore_lua_cleanup(void) +{ +} + +static void * +lua_mem_alloc(void *uptr, void *ptr, size_t osize, size_t nsize) +{ + if (nsize == 0) { + kore_free(ptr); + return (NULL); + } + + return (kore_realloc(ptr, nsize)); +} + +static void +lua_symbol_resolve(struct lua_symbol *sym, lua_State **L) +{ + lua_rawgeti(sym->L, LUA_REGISTRYINDEX, sym->ref); + *L = sym->L; +} + +static int +lua_argument_get_bool(lua_State *L, const char *field) +{ + int ret; + + lua_pushstring(L, field); + ret = lua_gettable(L, -2); + + if (ret == LUA_TNIL) { + lua_pop(L, 1); + return (0); + } + + luaL_argcheck(L, ret == LUA_TBOOLEAN, 0, field); + + ret = lua_toboolean(L, -1); + lua_pop(L, 1); + + return (ret); +} + +static const char * +lua_argument_get_string(lua_State *L, const char *field) +{ + const char *v; + int type; + + lua_pushstring(L, field); + type = lua_gettable(L, -2); + + if (type == LUA_TNIL) { + lua_pop(L, 1); + return (NULL); + } + + luaL_argcheck(L, type == LUA_TSTRING, 0, field); + + v = lua_tostring(L, -1); + lua_pop(L, 1); + + return (v); +} + +static int +lua_kore_module_init(lua_State *L) +{ + int i; + + luaL_newlib(L, lua_kore_functions); + + for (i = 0; lua_integers[i].symbol != NULL; i++) { + lua_pushstring(L, lua_integers[i].symbol); + lua_pushnumber(L, lua_integers[i].value); + lua_settable(L, -3); + } + + return (1); +} + +static void +lua_module_free(struct kore_module *module) +{ + struct lua_symbol *sym; + struct lua_module *lua; + + lua = module->handle; + + while ((sym = LIST_FIRST(&lua->symbols)) != NULL) { + LIST_REMOVE(sym, list); + kore_free(sym); + } + + kore_free(lua); +} + +static void +lua_module_reload(struct kore_module *module) +{ + lua_module_free(module); + lua_module_load(module); +} + +static void +lua_module_load(struct kore_module *module) +{ + struct lua_module *lua; + + lua = kore_calloc(1, sizeof(*lua)); + LIST_INIT(&lua->symbols); + + if ((lua->L = lua_newstate(lua_mem_alloc, NULL)) == NULL) + fatal("luaL_newstate"); + + luaL_openlibs(lua->L); + + luaL_requiref(lua->L, "kore", lua_kore_module_init, 1); + lua_pop(lua->L, 1); + + luaL_newmetatable(lua->L, "http_request"); + luaL_setfuncs(lua->L, lua_http_request_meta, 0); + lua_pop(lua->L, 1); + + lua_pushliteral(lua->L, "http_request_methods"); + luaL_newlib(lua->L, lua_http_request_methods); + lua_settable(lua->L, LUA_REGISTRYINDEX); + + luaL_newlib(lua->L, lua_http_request_methods); + lua_pop(lua->L, 1); + + if (luaL_loadfile(lua->L, module->path) != LUA_OK) { + fatal("%s: failed to import module (%s)", module->path, + lua_tostring(lua->L, -1)); + } + + if (lua_pcall(lua->L, 0, 0, 0) != LUA_OK) { + fatal("%s: failed to import module (%s)", module->path, + lua_tostring(lua->L, -1)); + } + + module->handle = lua; +} + +static void * +lua_module_getsym(struct kore_module *module, const char *symbol) +{ + int ref; + struct lua_module *lua; + struct lua_symbol *sym; + + lua = module->handle; + + if (lua_getglobal(lua->L, symbol) != LUA_TFUNCTION) + return (NULL); + + if ((ref = luaL_ref(lua->L, LUA_REGISTRYINDEX)) == LUA_REFNIL) + return (NULL); + + sym = kore_calloc(1, sizeof(*sym)); + + sym->ref = ref; + sym->L = lua->L; + + LIST_INSERT_HEAD(&lua->symbols, sym, list); + + return (sym); +} + +static int +lua_runtime_resolve(const char *module, const struct stat *st) +{ + const char *ext; + + if (!S_ISREG(st->st_mode)) + return (KORE_RESULT_ERROR); + + ext = strrchr(module, '.'); + + if (ext == NULL || strcasecmp(ext, ".lua")) + return (KORE_RESULT_ERROR); + + kore_module_load(module, NULL, KORE_MODULE_LUA); + + return (KORE_RESULT_OK); +} + +static int +lua_runtime_http_request(void *addr, struct http_request *req) +{ + lua_State *L; + struct lua_http_request *lreq; + + lua_symbol_resolve(addr, &L); + + lreq = lua_newuserdata(L, sizeof(*lreq)); + luaL_setmetatable(L, "http_request"); + + lreq->req = req; + + if (lua_pcall(L, 1, 0, 0)) { + kore_log(LOG_NOTICE, "%s: failed to call handler: %s", __func__, + lua_tostring(L, -1)); + http_response(req, 500, NULL, 0); + return (KORE_RESULT_OK); + } + + return (KORE_RESULT_OK); +} + +static void +lua_runtime_http_request_free(void *addr, struct http_request *req) +{ + fatal("%s: not yet implemented", __func__); +} + +static void +lua_runtime_http_body_chunk(void *addr, struct http_request *req, + const void *data, size_t len) +{ + fatal("%s: not yet implemented", __func__); +} + +static int +lua_runtime_validator(void *addr, struct http_request *req, const void *data) +{ + fatal("%s: not yet implemented", __func__); + + return (KORE_RESULT_ERROR); +} + +static void +lua_runtime_wsmessage(void *addr, struct connection *c, u_int8_t op, + const void *data, size_t len) +{ + fatal("%s: not yet implemented", __func__); +} + +static void +lua_runtime_execute(void *addr) +{ + lua_State *L; + + lua_symbol_resolve(addr, &L); + + if (lua_pcall(L, 0, 0, 0)) { + fatal("failed to execute function: %s", + lua_tostring(L, -1)); + } +} + +static void +lua_runtime_configure(void *addr, int argc, char **argv) +{ + lua_State *L; + int idx; + + lua_symbol_resolve(addr, &L); + + lua_pushinteger(L, argc); + lua_newtable(L); + + for (idx = 0; idx < argc; idx++) { + lua_pushstring(L, argv[idx]); + lua_rawseti(L, -2, idx); + } + + if (lua_pcall(L, 2, 0, 0)) { + fatal("failed to configure your application (%s)", + lua_tostring(L, -1)); + } +} + +static int +lua_runtime_onload(void *addr, int action) +{ + fatal("%s: not yet implemented", __func__); + + return (KORE_RESULT_ERROR); +} + +static void +lua_runtime_connect(void *addr, struct connection *c) +{ + fatal("%s: not yet implemented", __func__); +} + +static void +lua_runtime_signal(void *addr, int sig) +{ + fatal("%s: not yet implemented", __func__); +} + +static int +lua_kore_config(lua_State *L) +{ + char *v; + const char *opt, *val; + + lua_pushnil(L); + + while (lua_next(L, -2) != 0) { + if (!lua_isstring(L, -2)) + fatal("kore.config: keyword not a string"); + + opt = lua_tostring(L, -2); + + if (lua_isinteger(L, -1)) { + lua_pushvalue(L, -1); + val = lua_tostring(L, -1); + lua_pop(L, 1); + } else if (lua_isstring(L, -1)) { + val = lua_tostring(L, -1); + } else { + fatal("kore.config: value not a string or integer"); + } + + v = kore_strdup(val); + + if (!kore_configure_setting(opt, v)) { + kore_free(v); + luaL_error(L, "kore.config: cannot be set at runtime"); + lua_pop(L, 1); + return (0); + } + + kore_free(v); + lua_pop(L, 1); + } + + return (0); +} + +static int +lua_kore_server(lua_State *L) +{ + struct kore_server *srv; + const char *name, *ip, *port; + + if ((name = lua_argument_get_string(L, "name")) == NULL) + name = "default"; + + if ((ip = lua_argument_get_string(L, "ip")) == NULL) { + luaL_error(L, "kore.server: missing ip keyword"); + return (0); + } + + if ((port = lua_argument_get_string(L, "port")) == NULL) { + luaL_error(L, "kore.server: missing port keyword"); + return (0); + } + + if ((srv = kore_server_lookup(name)) != NULL) { + luaL_error(L, "kore.server: server '%s' exists", name); + return (0); + } + + srv = kore_server_create(name); + srv->tls = lua_argument_get_bool(L, "tls"); + + if (srv->tls && !kore_tls_supported()) { + kore_server_free(srv); + luaL_error(L, "kore.server: TLS not supported"); + return (0); + } + + if (!kore_server_bind(srv, ip, port, NULL)) { + kore_server_free(srv); + luaL_error(L, "kore.server: failed to bind %s:%s", ip, port); + return (0); + } + + kore_server_finalize(srv); + + return (0); +} + +static int +lua_http_request_gc(lua_State *L) +{ + struct lua_http_request *lreq; + + lreq = luaL_checkudata(L, 1, "http_request"); + kore_free(lreq); + + return (0); +} + +static int +lua_http_request_index(lua_State *L) +{ + struct lua_http_request *lreq; + const char *field; + + lreq = luaL_checkudata(L, 1, "http_request"); + field = luaL_checkstring(L, 2); + + lua_getfield(L, LUA_REGISTRYINDEX, "http_request_methods"); + lua_getfield(L, -1, field); + + if (!lua_isnil(L, -1)) + return (1); + + lua_pop(L, 2); + + if (!strcmp(field, "path")) { + lua_pushstring(L, lreq->req->path); + return (1); + } else if (!strcmp(field, "host")) { + lua_pushstring(L, lreq->req->host); + return (1); + } else if (!strcmp(field, "agent")) { + lua_pushstring(L, lreq->req->agent); + return (1); + } else if (!strcmp(field, "referer")) { + lua_pushstring(L, lreq->req->referer); + return (1); + } else if (!strcmp(field, "method")) { + lua_pushinteger(L, lreq->req->method); + return (1); + } + + return (0); +} + +static int +lua_http_response_header(lua_State *L) +{ + struct lua_http_request *lreq; + const char *header, *value; + + lreq = luaL_checkudata(L, 1, "http_request"); + header = luaL_checkstring(L, 2); + value = luaL_checkstring(L, 3); + + http_response_header(lreq->req, header, value); + + return (0); +} + +static int +lua_http_request_header(lua_State *L) +{ + struct lua_http_request *lreq; + const char *header, *value; + + lreq = luaL_checkudata(L, 1, "http_request"); + header = luaL_checkstring(L, 2); + + if (!http_request_header(lreq->req, header, &value)) { + lua_pushnil(L); + } else { + lua_pushstring(L, value); + } + + return (1); +} + +static int +lua_http_response(lua_State *L) +{ + size_t len; + struct lua_http_request *lreq; + const void *data; + int status; + + lreq = luaL_checkudata(L, 1, "http_request"); + status = luaL_checkinteger(L, 2); + + if (lua_isnil(L, 3)) { + len = 0; + data = NULL; + } else { + data = luaL_checklstring(L, 3, &len); + } + + http_response(lreq->req, status, data, len); + + return (0); +} diff --git a/src/mem.c b/src/mem.c index e16b402..d4305e3 100644 --- a/src/mem.c +++ b/src/mem.c @@ -14,35 +14,32 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include +/* + * The memory facitilies such as kore_malloc / kore_calloc are all + * based on the kore pool system as long as the allocations are + * below 8192 bytes. + * + * Anything over 8192 bytes will get an mmap() allocation instead + * that does not benefit from the protections offered by the kore_pool API. + */ + #include +#include #include #include #include "kore.h" -#define KORE_MEM_BLOCKS 11 -#define KORE_MEM_BLOCK_SIZE_MAX 8192 -#define KORE_MEM_BLOCK_PREALLOC 128 +#define KORE_MEM_POOLS 11 +#define KORE_MEM_POOLS_PREALLOC 32 +#define KORE_MEM_POOLS_SIZE_MAX 8192 -#define KORE_MEM_ALIGN 16 -#define KORE_MEM_MAGIC 0xd0d0 - -#define KORE_MEM_TAGGED 0x0001 - -struct memsize { - size_t len; - size_t magic; -} __attribute__((packed)); +#define KORE_MEM_TAGGED 0x0001 struct meminfo { + size_t len; u_int16_t flags; - u_int16_t magic; -} __attribute__((packed)); - -struct memblock { - struct kore_pool pool; }; struct tag { @@ -51,36 +48,38 @@ struct tag { TAILQ_ENTRY(tag) list; }; -static inline struct memsize *memsize(void *); static inline struct meminfo *meminfo(void *); -static size_t memblock_index(size_t); +static void *mem_alloc(size_t); +static size_t mem_index(size_t); static TAILQ_HEAD(, tag) tags; static struct kore_pool tag_pool; -static struct memblock blocks[KORE_MEM_BLOCKS]; +static struct kore_pool mempools[KORE_MEM_POOLS]; void kore_mem_init(void) { + const char *opt; int i, len; char name[32]; - u_int32_t size, elm, mlen; + size_t size, elm, mlen; + + if ((opt = getenv("KORE_MEM_GUARD")) != NULL && !strcmp(opt, "1")) + kore_mem_guard = 1; size = 8; TAILQ_INIT(&tags); - kore_pool_init(&tag_pool, "tag_pool", sizeof(struct tag), 100); + kore_pool_init(&tag_pool, "tag_pool", sizeof(struct tag), 4); - for (i = 0; i < KORE_MEM_BLOCKS; i++) { - len = snprintf(name, sizeof(name), "block-%u", size); + for (i = 0; i < KORE_MEM_POOLS; i++) { + len = snprintf(name, sizeof(name), "block-%zu", size); if (len == -1 || (size_t)len >= sizeof(name)) fatal("kore_mem_init: snprintf"); - elm = (KORE_MEM_BLOCK_PREALLOC * 1024) / size; - mlen = sizeof(struct memsize) + size + - sizeof(struct meminfo) + KORE_MEM_ALIGN; - mlen = mlen & ~(KORE_MEM_ALIGN - 1); + elm = (KORE_MEM_POOLS_PREALLOC * 1024) / size; + mlen = sizeof(struct meminfo) + size; - kore_pool_init(&blocks[i].pool, name, mlen, elm); + kore_pool_init(&mempools[i], name, mlen, elm); size = size << 1; } @@ -91,60 +90,44 @@ kore_mem_cleanup(void) { int i; - for (i = 0; i < KORE_MEM_BLOCKS; i++) { - kore_pool_cleanup(&blocks[i].pool); + for (i = 0; i < KORE_MEM_POOLS; i++) { + kore_pool_cleanup(&mempools[i]); } } +void * +kore_mmap_region(size_t len) +{ + void *ptr; + + if ((ptr = mmap(NULL, len, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)) == MAP_FAILED) + fatal("%s: mmap: %s", __func__, errno_s); + + return (ptr); +} + void * kore_malloc(size_t len) { - void *ptr; - struct meminfo *mem; - struct memsize *size; - u_int8_t *addr; - size_t mlen, idx; - - if (len == 0) - len = 8; - - if (len <= KORE_MEM_BLOCK_SIZE_MAX) { - idx = memblock_index(len); - ptr = kore_pool_get(&blocks[idx].pool); - } else { - mlen = sizeof(struct memsize) + len + sizeof(struct meminfo); - if ((ptr = calloc(1, mlen)) == NULL) - fatal("kore_malloc(%zu): %d", len, errno); - } - - size = (struct memsize *)ptr; - size->len = len; - size->magic = KORE_MEM_MAGIC; - - addr = (u_int8_t *)ptr + sizeof(struct memsize); - - mem = (struct meminfo *)(addr + size->len); - mem->flags = 0; - mem->magic = KORE_MEM_MAGIC; - - return (addr); + return (mem_alloc(len)); } void * kore_realloc(void *ptr, size_t len) { - struct memsize *size; + struct meminfo *mem; void *nptr; if (ptr == NULL) { - nptr = kore_malloc(len); + nptr = mem_alloc(len); } else { - size = memsize(ptr); - if (len == size->len) + mem = meminfo(ptr); + if (len == mem->len) return (ptr); - nptr = kore_malloc(len); - memcpy(nptr, ptr, MIN(len, size->len)); - kore_free(ptr); + nptr = mem_alloc(len); + memcpy(nptr, ptr, MIN(len, mem->len)); + kore_free_zero(ptr); } return (nptr); @@ -160,18 +143,31 @@ kore_calloc(size_t memb, size_t len) fatal("kore_calloc(): memb * len > SIZE_MAX"); total = memb * len; - ptr = kore_malloc(total); + ptr = mem_alloc(total); memset(ptr, 0, total); return (ptr); } +void +kore_free_zero(void *ptr) +{ + struct meminfo *mem; + + if (ptr == NULL) + return; + + mem = meminfo(ptr); + kore_mem_zero(ptr, mem->len); + + kore_free(ptr); +} + void kore_free(void *ptr) { size_t idx; struct meminfo *mem; - struct memsize *size; u_int8_t *addr; if (ptr == NULL) @@ -183,14 +179,14 @@ kore_free(void *ptr) mem->flags &= ~KORE_MEM_TAGGED; } - size = memsize(ptr); - addr = (u_int8_t *)ptr - sizeof(struct memsize); + addr = (u_int8_t *)ptr - sizeof(struct meminfo); - if (size->len <= KORE_MEM_BLOCK_SIZE_MAX) { - idx = memblock_index(size->len); - kore_pool_put(&blocks[idx].pool, addr); + if (mem->len <= KORE_MEM_POOLS_SIZE_MAX) { + idx = mem_index(mem->len); + kore_pool_put(&mempools[idx], addr); } else { - free(addr); + if (munmap(addr, sizeof(*mem) + mem->len) == -1) + fatal("%s: munmap: %s", __func__, errno_s); } } @@ -201,7 +197,7 @@ kore_strdup(const char *str) char *nstr; len = strlen(str) + 1; - nstr = kore_malloc(len); + nstr = mem_alloc(len); (void)kore_strlcpy(nstr, str, len); return (nstr); @@ -212,7 +208,7 @@ kore_malloc_tagged(size_t len, u_int32_t tag) { void *ptr; - ptr = kore_malloc(len); + ptr = mem_alloc(len); kore_mem_tag(ptr, tag); return (ptr); @@ -278,8 +274,33 @@ kore_mem_zero(void *ptr, size_t len) } } +static void * +mem_alloc(size_t len) +{ + void *ptr; + struct meminfo *mem; + size_t mlen, idx; + + if (len == 0) + len = 8; + + if (len <= KORE_MEM_POOLS_SIZE_MAX) { + idx = mem_index(len); + ptr = kore_pool_get(&mempools[idx]); + } else { + mlen = sizeof(struct meminfo) + len; + ptr = kore_mmap_region(mlen); + } + + mem = (struct meminfo *)ptr; + mem->len = len; + mem->flags = 0; + + return ((u_int8_t *)ptr + sizeof(struct meminfo)); +} + static size_t -memblock_index(size_t len) +mem_index(size_t len) { size_t mlen, idx; @@ -290,36 +311,14 @@ memblock_index(size_t len) mlen = mlen << 1; } - if (idx > (KORE_MEM_BLOCKS - 1)) - fatal("kore_malloc: idx too high"); + if (idx > (KORE_MEM_POOLS - 1)) + fatal("mem_index: idx too high"); return (idx); } -static inline struct memsize * -memsize(void *ptr) -{ - struct memsize *ms; - - ms = (struct memsize *)((u_int8_t *)ptr - sizeof(*ms)); - - if (ms->magic != KORE_MEM_MAGIC) - fatal("%s: bad memsize magic (0x%zx)", __func__, ms->magic); - - return (ms); -} - static inline struct meminfo * meminfo(void *ptr) { - struct memsize *ms; - struct meminfo *info; - - ms = memsize(ptr); - info = (struct meminfo *)((u_int8_t *)ptr + ms->len); - - if (info->magic != KORE_MEM_MAGIC) - fatal("%s: bad meminfo magic (0x%x)", __func__, info->magic); - - return (info); + return ((struct meminfo *)((u_int8_t *)ptr - sizeof(struct meminfo))); } diff --git a/src/module.c b/src/module.c index d2eb6fc..d7baa1f 100644 --- a/src/module.c +++ b/src/module.c @@ -29,6 +29,10 @@ #include "python_api.h" #endif +#if defined(KORE_USE_LUA) +#include "lua_api.h" +#endif + static TAILQ_HEAD(, kore_module) modules; static void native_free(struct kore_module *); @@ -92,6 +96,12 @@ kore_module_load(const char *path, const char *onload, int type) module->fun = &kore_python_module; module->runtime = &kore_python_runtime; break; +#endif +#if defined(KORE_USE_LUA) + case KORE_MODULE_LUA: + module->fun = &kore_lua_module; + module->runtime = &kore_lua_runtime; + break; #endif default: fatal("kore_module_load: unknown type %d", type); diff --git a/src/pool.c b/src/pool.c index d75045d..61099c3 100644 --- a/src/pool.c +++ b/src/pool.c @@ -14,6 +14,33 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +/* + * A kore_pool is a memory pool containing fixed-sized objects that + * can quickly be obtained by a caller via kore_pool_get() and returned + * via kore_pool_put(). + * + * Each entry in a pool will have a canary at the end that is used to + * catch any potential overruns when the entry is returned to the pool. + * + * If memory pool guards are enabled three additional things happen: + * + * 1) The metadata is placed at the start of a page instead + * of right before the returned user pointer. + * + * 2) Each pool entry gets a guard page at the end of its allocation + * that is marked as PROT_NONE. Touching a guard page will cause + * the application to receive a SIGSEGV. + * + * 3) Entries are only marked PROT_READ | PROT_WRITE when they are + * obtained with kore_pool_get(). Their memory protection is + * changed to PROT_NONE when returned to the pool via kore_pool_get(). + * + * Caveats: + * Pools are designed to live for the entire lifetime of a Kore process + * until it will exit and are therefor not properly cleaned up when exit + * time arrives. + */ + #include #include #include @@ -32,156 +59,230 @@ static void pool_lock(struct kore_pool *); static void pool_unlock(struct kore_pool *); #endif -static void pool_region_create(struct kore_pool *, size_t); -static void pool_region_destroy(struct kore_pool *); +static void pool_grow(struct kore_pool *, size_t); + +static void pool_mark_entry_rw(struct kore_pool *, void *); +static void pool_mark_entry_none(struct kore_pool *, void *); void kore_pool_init(struct kore_pool *pool, const char *name, size_t len, size_t elm) { + long pagesz; + if (elm < POOL_MIN_ELEMENTS) elm = POOL_MIN_ELEMENTS; + if ((pagesz = sysconf(_SC_PAGESIZE)) == -1) + fatal("%s: sysconf: %s", __func__, errno_s); + if ((pool->name = strdup(name)) == NULL) fatal("kore_pool_init: strdup %s", errno_s); - len = (len + (8 - 1)) & ~(8 - 1); + pool->uselen = len; + + len = len + sizeof(u_int64_t); + len = (len + (16 - 1)) & ~(16 - 1); + + pool->elmlen = len; pool->lock = 0; - pool->elms = 0; - pool->inuse = 0; - pool->elen = len; + pool->freelist = NULL; + pool->pagesz = pagesz; pool->growth = elm * 0.25f; - pool->slen = pool->elen + sizeof(struct kore_pool_entry); + pool->canary = (u_int64_t)kore_platform_random_uint32() << 32 | + kore_platform_random_uint32(); - LIST_INIT(&(pool->regions)); - LIST_INIT(&(pool->freelist)); + if (kore_mem_guard) { + pool->memsz = pool->pagesz * 2; - pool_region_create(pool, elm); + while (pool->elmlen > + pool->pagesz - sizeof(struct kore_pool_entry)) { + pool->memsz += pool->pagesz; + pool->elmlen -= MIN(pool->elmlen, pool->pagesz); + } + + pool->elmlen = len; + } else { + pool->memsz = pool->elmlen; + } + + pool_grow(pool, elm); } void kore_pool_cleanup(struct kore_pool *pool) { - pool->lock = 0; - pool->elms = 0; - pool->inuse = 0; - pool->elen = 0; - pool->slen = 0; + struct kore_pool_entry *entry, *next; + + if (kore_mem_guard) { + for (entry = pool->freelist; entry != NULL; entry = next) { + pool_mark_entry_rw(pool, entry); + next = entry->nextfree; + (void)munmap(entry, pool->memsz); + } + } free(pool->name); - pool->name = NULL; - - pool_region_destroy(pool); } void * kore_pool_get(struct kore_pool *pool) { - u_int8_t *ptr; + u_int64_t canary; struct kore_pool_entry *entry; #if defined(KORE_USE_TASKS) pool_lock(pool); #endif - if (LIST_EMPTY(&(pool->freelist))) - pool_region_create(pool, pool->growth); + if (pool->freelist == NULL) + pool_grow(pool, pool->growth); + + entry = pool->freelist; + + if (kore_mem_guard) + pool_mark_entry_rw(pool, entry); + + pool->freelist = entry->nextfree; - entry = LIST_FIRST(&(pool->freelist)); if (entry->state != POOL_ELEMENT_FREE) fatal("%s: element %p was not free", pool->name, (void *)entry); - LIST_REMOVE(entry, list); + entry->nextfree = NULL; entry->state = POOL_ELEMENT_BUSY; - ptr = (u_int8_t *)entry + sizeof(struct kore_pool_entry); - pool->inuse++; + canary = pool->canary; + canary ^= (uintptr_t)entry; + canary ^= (uintptr_t)entry->uptr; + + memcpy(entry->canary, &canary, sizeof(canary)); #if defined(KORE_USE_TASKS) pool_unlock(pool); #endif - return (ptr); + return (entry->uptr); } void kore_pool_put(struct kore_pool *pool, void *ptr) { + void *base; + u_int64_t canary; struct kore_pool_entry *entry; #if defined(KORE_USE_TASKS) pool_lock(pool); #endif - entry = (struct kore_pool_entry *) - ((u_int8_t *)ptr - sizeof(struct kore_pool_entry)); + if (kore_mem_guard) { + base = (u_int8_t *)ptr - ((uintptr_t)ptr % pool->pagesz); + } else { + base = (u_int8_t *)ptr - sizeof(*entry); + } + + entry = (struct kore_pool_entry *)base; + + if (entry->uptr != ptr) { + fatal("%s: uptr mismatch %p != %p", + pool->name, entry->uptr, ptr); + } + + memcpy(&canary, entry->canary, sizeof(canary)); + canary ^= (uintptr_t)entry; + canary ^= (uintptr_t)ptr; + + if (canary != pool->canary) + fatal("%s: memory corruption detected", pool->name); if (entry->state != POOL_ELEMENT_BUSY) fatal("%s: element %p was not busy", pool->name, ptr); entry->state = POOL_ELEMENT_FREE; - LIST_INSERT_HEAD(&(pool->freelist), entry, list); + entry->nextfree = pool->freelist; - pool->inuse--; + if (kore_mem_guard) + pool_mark_entry_none(pool, entry); + pool->freelist = entry; #if defined(KORE_USE_TASKS) pool_unlock(pool); #endif } static void -pool_region_create(struct kore_pool *pool, size_t elms) +pool_grow(struct kore_pool *pool, size_t elms) { size_t i; - u_int8_t *p; - struct kore_pool_region *reg; - struct kore_pool_entry *entry; + u_int8_t *base, *p; + struct kore_pool_entry *entry, *prev; - if ((reg = calloc(1, sizeof(struct kore_pool_region))) == NULL) - fatal("pool_region_create: calloc: %s", errno_s); + prev = pool->freelist; - LIST_INSERT_HEAD(&(pool->regions), reg, list); - - if (SIZE_MAX / elms < pool->slen) - fatal("pool_region_create: overflow"); - - reg->length = elms * pool->slen; - reg->start = mmap(NULL, reg->length, PROT_READ | PROT_WRITE, - MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); - if (reg->start == MAP_FAILED) - fatal("mmap: %s", errno_s); - - p = (u_int8_t *)reg->start; + if (kore_mem_guard == 0) + base = kore_mmap_region(elms * (sizeof(*entry) + pool->elmlen)); + else + base = NULL; for (i = 0; i < elms; i++) { - entry = (struct kore_pool_entry *)p; - entry->region = reg; - entry->state = POOL_ELEMENT_FREE; - LIST_INSERT_HEAD(&(pool->freelist), entry, list); + if (kore_mem_guard) { + base = kore_mmap_region(pool->memsz); + p = base + (pool->memsz - pool->pagesz - pool->elmlen); + entry = (struct kore_pool_entry *)base; + } else { + p = base + ((sizeof(*entry) + pool->elmlen) * i); + entry = (struct kore_pool_entry *)p; + p += sizeof(*entry); + } - p = p + pool->slen; + entry->uptr = p; + entry->nextfree = NULL; + entry->state = POOL_ELEMENT_FREE; + entry->canary = p + pool->uselen; + + if (prev != NULL) { + prev->nextfree = entry; + if (kore_mem_guard) + pool_mark_entry_none(pool, prev); + } + + prev = entry; + + if (pool->freelist == NULL) + pool->freelist = entry; + + if (kore_mem_guard) { + p += pool->elmlen; + + if (((uintptr_t)p % pool->pagesz) != 0) + fatal("%s: misaligned page", __func__); + + if (mprotect(p, pool->pagesz, PROT_NONE) == -1) + fatal("%s: mprotect: %s", __func__, errno_s); + + if (madvise(p, pool->pagesz, MADV_FREE) == -1) + fatal("%s: madvise: %s", __func__, errno_s); + } } - pool->elms += elms; + if (prev != NULL && kore_mem_guard) + pool_mark_entry_none(pool, prev); } static void -pool_region_destroy(struct kore_pool *pool) +pool_mark_entry_none(struct kore_pool *pool, void *ptr) { - struct kore_pool_region *reg; + if (mprotect(ptr, pool->memsz - pool->pagesz, PROT_NONE) == -1) + fatal("%s: mprotect: %s", __func__, errno_s); +} - /* Take care iterating when modifying list contents */ - while (!LIST_EMPTY(&pool->regions)) { - reg = LIST_FIRST(&pool->regions); - LIST_REMOVE(reg, list); - (void)munmap(reg->start, reg->length); - free(reg); - } - - /* Freelist references into the regions memory allocations */ - LIST_INIT(&pool->freelist); - pool->elms = 0; +static void +pool_mark_entry_rw(struct kore_pool *pool, void *ptr) +{ + if (mprotect(ptr, pool->memsz - pool->pagesz, + PROT_READ | PROT_WRITE) == -1) + fatal("%s: mprotect: %s", __func__, errno_s); } #if defined(KORE_USE_TASKS) diff --git a/src/python.c b/src/python.c index ab0f3c6..d81448d 100644 --- a/src/python.c +++ b/src/python.c @@ -55,6 +55,10 @@ #include +#if PY_VERSION_HEX >= 0x030b0000 +#include +#endif + #if PY_VERSION_HEX < 0x030A0000 typedef enum { PYGEN_RETURN = 0, @@ -68,11 +72,17 @@ struct reqcall { TAILQ_ENTRY(reqcall) list; }; +union deconst { + char *p; + const char *cp; +}; + TAILQ_HEAD(reqcall_list, reqcall); PyMODINIT_FUNC python_module_init(void); static PyObject *python_import(const char *); +static int python_resolve_frame_line(void *); static PyObject *pyconnection_alloc(struct connection *); static PyObject *python_callable(PyObject *, const char *); static void python_split_arguments(char *, char **, size_t); @@ -157,6 +167,7 @@ static void python_push_integer(PyObject *, const char *, long); static void python_push_type(const char *, PyObject *, PyTypeObject *); static int python_validator_check(PyObject *); +static int python_runtime_resolve(const char *, const struct stat *); static int python_runtime_http_request(void *, struct http_request *); static void python_runtime_http_request_free(void *, struct http_request *); static void python_runtime_http_body_chunk(void *, struct http_request *, @@ -190,6 +201,7 @@ struct kore_module_functions kore_python_module = { struct kore_runtime kore_python_runtime = { KORE_RUNTIME_PYTHON, + .resolve = python_runtime_resolve, .http_request = python_runtime_http_request, .http_body_chunk = python_runtime_http_body_chunk, .http_request_free = python_runtime_http_request_free, @@ -320,7 +332,7 @@ static PyObject *python_tracer = NULL; static struct python_coro *coro_running = NULL; #if !defined(KORE_SINGLE_BINARY) -const char *kore_pymodule = NULL; +static const char *kore_pymodule = NULL; #endif void @@ -1179,21 +1191,49 @@ python_coro_suspend(struct python_coro *coro) python_coro_trace("suspended", coro); } +static int +python_resolve_frame_line(void *ptr) +{ + int line; +#if PY_VERSION_HEX >= 0x030b0000 + int addr; + _PyInterpreterFrame *frame; + + frame = ptr; + addr = _PyInterpreterFrame_LASTI(frame) * sizeof(_Py_CODEUNIT); + line = PyCode_Addr2Line(frame->f_code, addr); +#else + line = PyFrame_GetLineNumber(ptr); +#endif + + return (line); +} + static void python_coro_trace(const char *label, struct python_coro *coro) { int line; - PyGenObject *gen; + PyCoroObject *obj; PyCodeObject *code; +#if PY_VERSION_HEX >= 0x030b0000 + _PyInterpreterFrame *frame; +#else + PyFrameObject *frame; +#endif const char *func, *fname, *file; if (coro_tracing == 0) return; - gen = (PyGenObject *)coro->obj; + obj = (PyCoroObject *)coro->obj; - if (gen->gi_frame != NULL && gen->gi_frame->f_code != NULL) { - code = gen->gi_frame->f_code; +#if PY_VERSION_HEX >= 0x030b0000 + frame = (_PyInterpreterFrame *)obj->cr_iframe; +#else + frame = obj->cr_frame; +#endif + if (frame != NULL && frame->f_code != NULL) { + code = frame->f_code; func = PyUnicode_AsUTF8AndSize(code->co_name, NULL); file = PyUnicode_AsUTF8AndSize(code->co_filename, NULL); @@ -1206,8 +1246,8 @@ python_coro_trace(const char *label, struct python_coro *coro) fname = "unknown"; } - if (gen->gi_frame != NULL) - line = PyFrame_GetLineNumber(gen->gi_frame); + if (frame != NULL) + line = python_resolve_frame_line(frame); else line = -1; @@ -1240,6 +1280,38 @@ pyhttp_file_dealloc(struct pyhttp_file *pyfile) PyObject_Del((PyObject *)pyfile); } +static int +python_runtime_resolve(const char *module, const struct stat *st) +{ + const char *ext; + + if (!S_ISDIR(st->st_mode) && !S_ISREG(st->st_mode)) + return (KORE_RESULT_ERROR); + + if (S_ISDIR(st->st_mode)) { + kore_module_load(module, NULL, KORE_MODULE_PYTHON); + if (chdir(module) == -1) + fatal("chdir(%s): %s", module, errno_s); + } else { + if ((ext = strrchr(module, '.')) == NULL) + return (KORE_RESULT_ERROR); + + if (strcasecmp(ext, ".py")) + return (KORE_RESULT_ERROR); + + kore_module_load(module, NULL, KORE_MODULE_PYTHON); + } + +#if !defined(KORE_SINGLE_BINARY) + kore_pymodule = module; +#endif + + kore_hooks_set(KORE_PYTHON_CONFIG_HOOK, + KORE_PYTHON_TEARDOWN_HOOK, KORE_PYTHON_DAEMONIZED_HOOK); + + return (KORE_RESULT_OK); +} + static int python_runtime_http_request(void *addr, struct http_request *req) { @@ -2689,12 +2761,15 @@ python_kore_timer(PyObject *self, PyObject *args, PyObject *kwargs) } static PyObject * -python_kore_proc(PyObject *self, PyObject *args) +python_kore_proc(PyObject *self, PyObject *args, PyObject *kwargs) { + union deconst cp; const char *cmd; struct pyproc *proc; - char *copy, *argv[32], *env[1]; + Py_ssize_t idx, len; + PyObject *obj, *item; int timeo, in_pipe[2], out_pipe[2]; + char *copy, *argv[32], *env[PYTHON_PROC_MAX_ENV + 1]; timeo = -1; @@ -2707,6 +2782,37 @@ python_kore_proc(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "s|i", &cmd, &timeo)) return (NULL); + if (kwargs != NULL && + (obj = PyDict_GetItemString(kwargs, "env")) != NULL) { + if (!PyList_CheckExact(obj)) { + PyErr_SetString(PyExc_RuntimeError, + "kore.proc: env is not of type 'list'"); + return (NULL); + } + + len = PyList_Size(obj); + if (len > PYTHON_PROC_MAX_ENV) { + PyErr_SetString(PyExc_RuntimeError, + "kore.proc: too many entries in 'env' keyword"); + return (NULL); + } + + for (idx = 0; idx < len; idx++) { + if ((item = PyList_GetItem(obj, idx)) == NULL) + return (NULL); + + if (!PyUnicode_CheckExact(item)) + return (NULL); + + if ((cp.cp = PyUnicode_AsUTF8(item)) == NULL) + return (NULL); + + env[idx] = cp.p; + } + + env[idx] = NULL; + } + if (pipe(in_pipe) == -1) { PyErr_SetString(PyExc_RuntimeError, errno_s); return (NULL); @@ -2763,7 +2869,6 @@ python_kore_proc(PyObject *self, PyObject *args) dup2(in_pipe[0], STDIN_FILENO) == -1) fatal("dup2: %s", errno_s); - env[0] = NULL; copy = kore_strdup(cmd); python_split_arguments(copy, argv, 32); @@ -5022,6 +5127,46 @@ pyhttp_cookie(struct pyhttp_request *pyreq, PyObject *args) return (value); } +static PyObject * +pyhttp_headers(struct pyhttp_request *pyreq, PyObject *args) +{ + struct http_header *hdr; + struct http_request *req; + PyObject *obj, *dict, *ret; + + ret = NULL; + obj = NULL; + dict = NULL; + + req = pyreq->req; + + if ((dict = PyDict_New()) == NULL) + goto cleanup; + + if ((obj = PyUnicode_FromString(req->host)) == NULL) + goto cleanup; + + if (PyDict_SetItemString(dict, "host", obj) == -1) + goto cleanup; + + TAILQ_FOREACH(hdr, &req->req_headers, list) { + if ((obj = PyUnicode_FromString(hdr->value)) == NULL) + goto cleanup; + if (PyDict_SetItemString(dict, hdr->header, obj) == -1) + goto cleanup; + } + + ret = dict; + obj = NULL; + dict = NULL; + +cleanup: + Py_XDECREF(obj); + Py_XDECREF(dict); + + return (ret); +} + static PyObject * pyhttp_file_lookup(struct pyhttp_request *pyreq, PyObject *args) { @@ -5473,6 +5618,23 @@ pydomain_filemaps(struct pydomain *domain, PyObject *args) Py_RETURN_NONE; } +static PyObject * +pydomain_redirect(struct pydomain *domain, PyObject *args) +{ + int status; + const char *src, *dst; + + if (!PyArg_ParseTuple(args, "sis", &src, &status, &dst)) + return (NULL); + + if (!http_redirect_add(domain->config, src, status, dst)) { + fatal("failed to add redirect '%s' on '%s'", + src, domain->config->domain); + } + + Py_RETURN_NONE; +} + static PyObject * pydomain_route(struct pydomain *domain, PyObject *args, PyObject *kwargs) { @@ -6328,6 +6490,7 @@ static PyObject * pycurl_handle_setopt_string(struct pycurl_data *data, int idx, PyObject *obj) { const char *str; + CURLoption option; if (!PyUnicode_Check(obj)) { PyErr_Format(PyExc_RuntimeError, @@ -6339,8 +6502,8 @@ pycurl_handle_setopt_string(struct pycurl_data *data, int idx, PyObject *obj) if ((str = PyUnicode_AsUTF8(obj)) == NULL) return (NULL); - curl_easy_setopt(data->curl.handle, - CURLOPTTYPE_OBJECTPOINT + py_curlopt[idx].value, str); + option = CURLOPTTYPE_OBJECTPOINT + py_curlopt[idx].value; + curl_easy_setopt(data->curl.handle, option, str); Py_RETURN_TRUE; } @@ -6349,6 +6512,7 @@ static PyObject * pycurl_handle_setopt_long(struct pycurl_data *data, int idx, PyObject *obj) { long val; + CURLoption option; if (!PyLong_CheckExact(obj)) { PyErr_Format(PyExc_RuntimeError, @@ -6362,8 +6526,8 @@ pycurl_handle_setopt_long(struct pycurl_data *data, int idx, PyObject *obj) if (val == -1 && PyErr_Occurred()) return (NULL); - curl_easy_setopt(data->curl.handle, - CURLOPTTYPE_LONG + py_curlopt[idx].value, val); + option = CURLOPTTYPE_LONG + py_curlopt[idx].value; + curl_easy_setopt(data->curl.handle, option, val); Py_RETURN_TRUE; } @@ -6375,6 +6539,7 @@ pycurl_handle_setopt_slist(struct pycurl_data *data, int idx, PyObject *obj) PyObject *item; const char *sval; struct curl_slist *slist; + CURLoption option; Py_ssize_t list_len, i; if (!PyList_CheckExact(obj)) { @@ -6405,8 +6570,8 @@ pycurl_handle_setopt_slist(struct pycurl_data *data, int idx, PyObject *obj) psl->slist = slist; LIST_INSERT_HEAD(&data->slists, psl, list); - curl_easy_setopt(data->curl.handle, - CURLOPTTYPE_OBJECTPOINT + py_curlopt[idx].value, slist); + option = CURLOPTTYPE_OBJECTPOINT + py_curlopt[idx].value; + curl_easy_setopt(data->curl.handle, option, slist); Py_RETURN_TRUE; } diff --git a/src/runtime.c b/src/runtime.c index 939debf..8b942cb 100644 --- a/src/runtime.c +++ b/src/runtime.c @@ -27,6 +27,10 @@ #include "python_api.h" #endif +#if defined(KORE_USE_LUA) +#include "lua_api.h" +#endif + static void native_runtime_execute(void *); static int native_runtime_onload(void *, int); static void native_runtime_signal(void *, int); @@ -62,6 +66,41 @@ struct kore_runtime kore_native_runtime = { .configure = native_runtime_configure }; +static struct kore_runtime *runtimes[] = { +#if defined(KORE_USE_PYTHON) + &kore_python_runtime, +#endif +#if defined(KORE_USE_LUA) + &kore_lua_runtime, +#endif + NULL +}; + +const size_t +kore_runtime_count(void) +{ + return ((sizeof(runtimes) / sizeof(runtimes[0])) - 1); +} + +void +kore_runtime_resolve(const char *module, const struct stat *st) +{ + int i; + + if (runtimes[0] == NULL) + return; + + for (i = 0; runtimes[i] != NULL; i++) { + if (runtimes[i]->resolve == NULL) + continue; + if (runtimes[i]->resolve(module, st)) + break; + } + + if (runtimes[i] == NULL) + fatal("No runtime available to run '%s'", module); +} + struct kore_runtime_call * kore_runtime_getcall(const char *symbol) { diff --git a/src/seccomp.c b/src/seccomp.c index 1daf754..ba4240d 100644 --- a/src/seccomp.c +++ b/src/seccomp.c @@ -67,6 +67,9 @@ static struct sock_filter filter_kore[] = { KORE_SYSCALL_ALLOW(fstat), #if defined(SYS_fstat64) KORE_SYSCALL_ALLOW(fstat64), +#endif +#if defined(SYS_newfstatat) + KORE_SYSCALL_ALLOW(newfstatat), #endif KORE_SYSCALL_ALLOW(write), KORE_SYSCALL_ALLOW(fcntl), @@ -102,6 +105,9 @@ static struct sock_filter filter_kore[] = { KORE_SYSCALL_ALLOW(geteuid), KORE_SYSCALL_ALLOW(exit_group), KORE_SYSCALL_ALLOW(nanosleep), +#if defined(SYS_clock_gettime64) + KORE_SYSCALL_ALLOW(clock_gettime64), +#endif #if defined(SYS_clock_nanosleep) KORE_SYSCALL_ALLOW(clock_nanosleep), #endif diff --git a/src/tls_none.c b/src/tls_none.c index e167a2c..4eca272 100644 --- a/src/tls_none.c +++ b/src/tls_none.c @@ -41,7 +41,12 @@ kore_keymgr_cleanup(int final) void kore_tls_init(void) { - kore_log(LOG_ERR, "No compiled in TLS backend"); +} + +void +kore_tls_log_version(void) +{ + kore_log(LOG_NOTICE, "No compiled in TLS backend"); } void diff --git a/src/tls_openssl.c b/src/tls_openssl.c index c2fffbb..c06137a 100644 --- a/src/tls_openssl.c +++ b/src/tls_openssl.c @@ -36,11 +36,28 @@ #include "kore.h" #include "http.h" +/* + * Disable deprecated declaration warnings if we're building against + * OpenSSL 3 as they marked all low-level APIs as deprecated. + * + * Work is being done to replace these, but for now let things build. + */ +#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3 +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + #define TLS_SESSION_ID "kore_tls_sessionid" +/* Helper for weird API designs (looking at you OpenSSL). */ +union deconst { + void *p; + const void *cp; +}; + static int tls_domain_x509_verify(int, X509_STORE_CTX *); static X509 *tls_domain_load_certificate_chain(SSL_CTX *, const void *, size_t); +static EVP_PKEY *tls_privsep_private_key(EVP_PKEY *, struct kore_domain *); static int tls_sni_cb(SSL *, int *, void *); static void tls_info_callback(const SSL *, int, int); @@ -59,13 +76,8 @@ static int tls_keymgr_rsa_finish(RSA *); static int tls_keymgr_rsa_privenc(int, const unsigned char *, unsigned char *, RSA *, int); -static ECDSA_SIG *tls_keymgr_ecdsa_sign(const unsigned char *, int, - const BIGNUM *, const BIGNUM *, EC_KEY *); - -static RSA_METHOD *keymgr_rsa_meth = NULL; -static EC_KEY_METHOD *keymgr_ec_meth = NULL; - static DH *dh_params = NULL; +static RSA_METHOD *keymgr_rsa_meth = NULL; static int tls_version = KORE_TLS_VERSION_BOTH; static char *tls_cipher_list = KORE_DEFAULT_CIPHER_LIST; @@ -94,21 +106,13 @@ kore_tls_init(void) SSL_load_error_strings(); ERR_load_crypto_strings(); - if ((keymgr_rsa_meth = RSA_meth_new("kore RSA keymgr method", - RSA_METHOD_FLAG_NO_CHECK)) == NULL) - fatal("failed to setup RSA method"); - - RSA_meth_set_init(keymgr_rsa_meth, tls_keymgr_rsa_init); - RSA_meth_set_finish(keymgr_rsa_meth, tls_keymgr_rsa_finish); - RSA_meth_set_priv_enc(keymgr_rsa_meth, tls_keymgr_rsa_privenc); - - if ((keymgr_ec_meth = EC_KEY_METHOD_new(NULL)) == NULL) - fatal("failed to allocate EC KEY method"); - - EC_KEY_METHOD_set_sign(keymgr_ec_meth, - NULL, NULL, tls_keymgr_ecdsa_sign); +} +void +kore_tls_log_version(void) +{ kore_log(LOG_NOTICE, "TLS backend %s", OPENSSL_VERSION_TEXT); + #if !defined(TLS1_3_VERSION) if (!kore_quiet) { kore_log(LOG_NOTICE, @@ -122,7 +126,6 @@ void kore_tls_cleanup(void) { RSA_meth_free(keymgr_rsa_meth); - EC_KEY_METHOD_free(keymgr_ec_meth); } void @@ -188,6 +191,14 @@ kore_tls_keymgr_init(void) if ((meth = RSA_get_default_method()) == NULL) fatal("failed to obtain RSA method"); + if ((keymgr_rsa_meth = RSA_meth_new("kore RSA keymgr method", + RSA_METHOD_FLAG_NO_CHECK)) == NULL) + fatal("failed to setup RSA method"); + + RSA_meth_set_init(keymgr_rsa_meth, tls_keymgr_rsa_init); + RSA_meth_set_finish(keymgr_rsa_meth, tls_keymgr_rsa_finish); + RSA_meth_set_priv_enc(keymgr_rsa_meth, tls_keymgr_rsa_privenc); + RSA_meth_set_pub_enc(keymgr_rsa_meth, RSA_meth_get_pub_enc(meth)); RSA_meth_set_pub_dec(keymgr_rsa_meth, RSA_meth_get_pub_dec(meth)); RSA_meth_set_bn_mod_exp(keymgr_rsa_meth, RSA_meth_get_bn_mod_exp(meth)); @@ -200,11 +211,9 @@ kore_tls_domain_setup(struct kore_domain *dom, int type, const void *data, size_t datalen) { const u_int8_t *ptr; - RSA *rsa; - X509 *x509; EVP_PKEY *pkey; + X509 *x509; STACK_OF(X509_NAME) *certs; - EC_KEY *eckey; const SSL_METHOD *method; if (dom->tls_ctx != NULL) @@ -280,16 +289,7 @@ kore_tls_domain_setup(struct kore_domain *dom, int type, switch (EVP_PKEY_id(pkey)) { case EVP_PKEY_RSA: - if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL) - fatalx("no RSA public key present"); - RSA_set_app_data(rsa, dom); - RSA_set_method(rsa, keymgr_rsa_meth); - break; - case EVP_PKEY_EC: - if ((eckey = EVP_PKEY_get1_EC_KEY(pkey)) == NULL) - fatalx("no EC public key present"); - EC_KEY_set_ex_data(eckey, 0, dom); - EC_KEY_set_method(eckey, keymgr_ec_meth); + pkey = tls_privsep_private_key(pkey, dom); break; default: fatalx("unknown public key in certificate"); @@ -358,7 +358,9 @@ kore_tls_domain_crl(struct kore_domain *dom, const void *pem, size_t pemlen) int err; BIO *in; X509_CRL *crl; + X509_REVOKED *rev; X509_STORE *store; + struct connection *c, *next; ERR_clear_error(); in = BIO_new_mem_buf(pem, pemlen); @@ -369,6 +371,9 @@ kore_tls_domain_crl(struct kore_domain *dom, const void *pem, size_t pemlen) return; } + X509_STORE_set_flags(store, + X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); + for (;;) { crl = PEM_read_bio_X509_CRL(in, NULL, NULL, NULL); if (crl == NULL) { @@ -395,12 +400,42 @@ kore_tls_domain_crl(struct kore_domain *dom, const void *pem, size_t pemlen) X509_CRL_free(crl); continue; } + + /* + * Check if any accepted connection authenticated themselves + * with a now revoked certificate. + */ + for (c = TAILQ_FIRST(&connections); c != NULL; c = next) { + next = TAILQ_NEXT(c, list); + if (c->proto != CONN_PROTO_HTTP) + continue; + + /* + * Prune any connection that is currently not yet done + * with the TLS handshake. This is to prevent a race + * where a handshake could not yet be complete but + * did pass the x509 verification step and their cert + * was revoked in this CRL update. + */ + if (c->state == CONN_STATE_TLS_SHAKE) { + kore_connection_disconnect(c); + continue; + } + + if (c->tls_cert == NULL) + continue; + + if (X509_CRL_get0_by_cert(crl, &rev, c->tls_cert) != 1) + continue; + + kore_connection_log(c, + "connection removed, its certificate is revoked"); + + kore_connection_disconnect(c); + } } BIO_free(in); - - X509_STORE_set_flags(store, - X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); } void @@ -416,13 +451,13 @@ kore_tls_connection_accept(struct connection *c) int r; if (primary_dom == NULL) { - kore_log(LOG_NOTICE, + kore_connection_log(c, "TLS handshake but no TLS configured on server"); return (KORE_RESULT_ERROR); } if (primary_dom->tls_ctx == NULL) { - kore_log(LOG_NOTICE, + kore_connection_log(c, "TLS configuration for %s not yet complete", primary_dom->domain); return (KORE_RESULT_ERROR); @@ -436,8 +471,11 @@ kore_tls_connection_accept(struct connection *c) SSL_set_fd(c->tls, c->fd); SSL_set_accept_state(c->tls); - if (!SSL_set_ex_data(c->tls, 0, c)) + if (!SSL_set_ex_data(c->tls, 0, c)) { + kore_connection_log(c, + "SSL_set_ex_data: %s", ssl_errno_s); return (KORE_RESULT_ERROR); + } if (primary_dom->cafile != NULL) c->flags |= CONN_LOG_TLS_FAILURE; @@ -454,7 +492,7 @@ kore_tls_connection_accept(struct connection *c) return (KORE_RESULT_RETRY); default: if (c->flags & CONN_LOG_TLS_FAILURE) { - kore_log(LOG_NOTICE, + kore_connection_log(c, "SSL_accept: %s", ssl_errno_s); } return (KORE_RESULT_ERROR); @@ -463,7 +501,7 @@ kore_tls_connection_accept(struct connection *c) #if defined(KORE_USE_ACME) if (c->proto == CONN_PROTO_ACME_ALPN) { - kore_log(LOG_INFO, "disconnecting acme client"); + kore_connection_log(c, "disconnecting ACME client"); kore_connection_disconnect(c); return (KORE_RESULT_ERROR); } @@ -472,7 +510,7 @@ kore_tls_connection_accept(struct connection *c) if (SSL_get_verify_mode(c->tls) & SSL_VERIFY_PEER) { c->tls_cert = SSL_get_peer_certificate(c->tls); if (c->tls_cert == NULL) { - kore_log(LOG_NOTICE, "no peer certificate"); + kore_connection_log(c, "no peer certificate returned"); return (KORE_RESULT_ERROR); } } else { @@ -518,8 +556,8 @@ kore_tls_read(struct connection *c, size_t *bytes) /* FALLTHROUGH */ default: if (c->flags & CONN_LOG_TLS_FAILURE) { - kore_log(LOG_NOTICE, - "SSL_read(): %s", ssl_errno_s); + kore_connection_log(c, "SSL_read: %s", + ssl_errno_s); } return (KORE_RESULT_ERROR); } @@ -566,8 +604,8 @@ kore_tls_write(struct connection *c, size_t len, size_t *written) /* FALLTHROUGH */ default: if (c->flags & CONN_LOG_TLS_FAILURE) { - kore_log(LOG_NOTICE, - "SSL_write(): %s", ssl_errno_s); + kore_connection_log(c, + "SSL_write: %s", ssl_errno_s); } return (KORE_RESULT_ERROR); } @@ -652,8 +690,10 @@ kore_tls_x509_subject_name(struct connection *c) { X509_NAME *name; - if ((name = X509_get_subject_name(c->tls_cert)) == NULL) - kore_log(LOG_NOTICE, "X509_get_subject_name: %s", ssl_errno_s); + if ((name = X509_get_subject_name(c->tls_cert)) == NULL) { + kore_connection_log(c, + "X509_get_subject_name: %s", ssl_errno_s); + } return (name); } @@ -664,7 +704,7 @@ kore_tls_x509_issuer_name(struct connection *c) X509_NAME *name; if ((name = X509_get_issuer_name(c->tls_cert)) == NULL) - kore_log(LOG_NOTICE, "X509_get_issuer_name: %s", ssl_errno_s); + kore_connection_log(c, "X509_get_issuer_name: %s", ssl_errno_s); return (name); } @@ -737,7 +777,7 @@ kore_tls_x509_data(struct connection *c, u_int8_t **ptr, size_t *olen) u_int8_t *der, *pp; if ((len = i2d_X509(c->tls_cert, NULL)) <= 0) { - kore_log(LOG_NOTICE, "i2d_X509: %s", ssl_errno_s); + kore_connection_log(c, "i2d_X509: %s", ssl_errno_s); return (KORE_RESULT_ERROR); } @@ -746,7 +786,7 @@ kore_tls_x509_data(struct connection *c, u_int8_t **ptr, size_t *olen) if (i2d_X509(c->tls_cert, &pp) <= 0) { kore_free(der); - kore_log(LOG_NOTICE, "i2d_X509: %s", ssl_errno_s); + kore_connection_log(c, "i2d_X509: %s", ssl_errno_s); return (KORE_RESULT_ERROR); } @@ -894,53 +934,6 @@ tls_keymgr_rsa_finish(RSA *rsa) return (1); } -static ECDSA_SIG * -tls_keymgr_ecdsa_sign(const unsigned char *dgst, int dgst_len, - const BIGNUM *in_kinv, const BIGNUM *in_r, EC_KEY *eckey) -{ - size_t len; - ECDSA_SIG *sig; - const u_int8_t *ptr; - struct kore_domain *dom; - struct kore_keyreq *req; - - if (in_kinv != NULL || in_r != NULL) - return (NULL); - - len = sizeof(*req) + dgst_len; - if (len > sizeof(keymgr_buf)) - fatal("keymgr_buf too small"); - - if ((dom = EC_KEY_get_ex_data(eckey, 0)) == NULL) - fatal("EC_KEY has no domain"); - - memset(keymgr_buf, 0, sizeof(keymgr_buf)); - req = (struct kore_keyreq *)keymgr_buf; - - if (kore_strlcpy(req->domain, dom->domain, sizeof(req->domain)) >= - sizeof(req->domain)) - fatal("%s: domain truncated", __func__); - - req->data_len = dgst_len; - memcpy(&req->data[0], dgst, req->data_len); - - kore_msg_send(KORE_WORKER_KEYMGR, KORE_MSG_KEYMGR_REQ, keymgr_buf, len); - tls_keymgr_await_data(); - - if (keymgr_response) { - ptr = keymgr_buf; - sig = d2i_ECDSA_SIG(NULL, &ptr, keymgr_buflen); - } else { - sig = NULL; - } - - keymgr_buflen = 0; - keymgr_response = 0; - kore_platform_event_all(worker->msg[1]->fd, worker->msg[1]); - - return (sig); -} - static void tls_keymgr_await_data(void) { @@ -1044,9 +1037,18 @@ tls_keymgr_msg_response(struct kore_msg *msg, const void *data) static int tls_domain_x509_verify(int ok, X509_STORE_CTX *ctx) { - X509 *cert; - const char *text; - int error, depth; + struct connection *c; + SSL *tls; + X509 *cert; + const char *text; + int error, depth; + + if ((tls = X509_STORE_CTX_get_ex_data(ctx, + SSL_get_ex_data_X509_STORE_CTX_idx())) == NULL) + fatal("X509_STORE_CTX_get_ex_data: no data"); + + if ((c = SSL_get_ex_data(tls, 0)) == NULL) + fatal("no connection data in %s", __func__); error = X509_STORE_CTX_get_error(ctx); cert = X509_STORE_CTX_get_current_cert(ctx); @@ -1055,8 +1057,8 @@ tls_domain_x509_verify(int ok, X509_STORE_CTX *ctx) text = X509_verify_cert_error_string(error); depth = X509_STORE_CTX_get_error_depth(ctx); - kore_log(LOG_WARNING, "X509 verification error depth:%d - %s", - depth, text); + kore_connection_log(c, + "X509 verification error depth:%d - %s", depth, text); /* Continue on CRL validity errors. */ switch (error) { @@ -1113,6 +1115,41 @@ tls_domain_load_certificate_chain(SSL_CTX *ctx, const void *data, size_t len) return (x); } +static EVP_PKEY * +tls_privsep_private_key(EVP_PKEY *pub, struct kore_domain *dom) +{ + EVP_PKEY *pkey; + RSA *rsa_new; + const RSA *rsa_cur; + const BIGNUM *e_cur, *n_cur; + BIGNUM *e_new, *n_new; + + if ((pkey = EVP_PKEY_new()) == NULL) + fatalx("failed to create new EVP_PKEY data structure"); + + if ((rsa_new = RSA_new()) == NULL) + fatalx("failed to create new RSA data structure"); + + if ((rsa_cur = EVP_PKEY_get0_RSA(pub)) == NULL) + fatalx("no RSA public key present"); + + RSA_get0_key(rsa_cur, &n_cur, &e_cur, NULL); + + if ((n_new = BN_dup(n_cur)) == NULL) + fatalx("BN_dup failed"); + + if ((e_new = BN_dup(e_cur)) == NULL) + fatalx("BN_dup failed"); + + RSA_set_app_data(rsa_new, dom); + RSA_set_method(rsa_new, keymgr_rsa_meth); + RSA_set0_key(rsa_new, n_new, e_new, NULL); + + EVP_PKEY_set1_RSA(pkey, rsa_new); + + return (pkey); +} + #if defined(KORE_USE_ACME) static int tls_acme_alpn(SSL *ssl, const unsigned char **out, unsigned char *outlen, diff --git a/src/utils.c b/src/utils.c index 4d4f92c..d497fbe 100644 --- a/src/utils.c +++ b/src/utils.c @@ -239,10 +239,11 @@ kore_split_string(char *input, const char *delim, char **out, size_t ele) } void -kore_strip_chars(char *in, const char strip, char **out) +kore_strip_chars(const char *in, const char strip, char **out) { + char *p; + const char *s; u_int32_t len; - char *s, *p; len = strlen(in); *out = kore_malloc(len + 1); diff --git a/src/worker.c b/src/worker.c index 8ce7ea5..4700186 100644 --- a/src/worker.c +++ b/src/worker.c @@ -132,7 +132,7 @@ kore_worker_init(void) sizeof(*accept_lock)); memset(kore_workers, 0, sizeof(struct kore_worker) * worker_count); - if (worker_count > cpu_count) + if ((worker_count - 2) > cpu_count) kore_log(LOG_NOTICE, "more worker processes than cpu cores"); /* Setup log buffers. */ @@ -369,6 +369,9 @@ kore_worker_privsep(void) if (worker == NULL) fatalx("%s called with no worker", __func__); + if (worker_set_affinity == 1) + kore_platform_worker_setcpu(worker); + pw = NULL; /* Must happen before chroot. */ @@ -450,9 +453,6 @@ kore_worker_entry(struct kore_worker *kw) kore_platform_proctitle(kore_worker_name(kw->id)); - if (worker_set_affinity == 1) - kore_platform_worker_setcpu(kw); - kore_pid = kw->pid; kore_signal_setup();