diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/openssl.c | 232 |
1 files changed, 229 insertions, 3 deletions
diff --git a/src/openssl.c b/src/openssl.c index c1b5436..4e0b898 100644 --- a/src/openssl.c +++ b/src/openssl.c @@ -87,6 +87,10 @@ #define HAVE_SSL_CTX_SET_ALPN_PROTOS (OPENSSL_VERSION_NUMBER >= 0x1000200fL) #endif +#ifndef HAVE_SSL_CTX_SET_ALPN_SELECT_CB +#define HAVE_SSL_CTX_SET_ALPN_SELECT_CB HAVE_SSL_CTX_SET_ALPN_PROTOS +#endif + #ifndef HAVE_SSL_SET_ALPN_PROTOS #define HAVE_SSL_SET_ALPN_PROTOS HAVE_SSL_CTX_SET_ALPN_PROTOS #endif @@ -99,6 +103,25 @@ #define STRERROR_R_CHAR_P (defined __GLIBC__ && (_GNU_SOURCE || !(_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600))) #endif +#ifndef LIST_HEAD +#define LIST_HEAD(name, type) struct name { struct type *lh_first; } +#define LIST_ENTRY(type) struct { struct type *le_next, **le_prev; } +#define LIST_INIT(head) do { LIST_FIRST((head)) = NULL; } while (0) +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) +#define LIST_REMOVE(elm, field) do { \ + if (LIST_NEXT((elm), field) != NULL) \ + LIST_NEXT((elm), field)->field.le_prev = (elm)->field.le_prev; \ + *(elm)->field.le_prev = LIST_NEXT((elm), field); \ +} while (0) +#define LIST_INSERT_HEAD(head, elm, field) do { + if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ + LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field); \ + LIST_FIRST((head)) = (elm); \ + (elm)->field.le_prev = &LIST_FIRST((head)); \ +} while (0) +#endif + #define BIGNUM_CLASS "BIGNUM*" #define PKEY_CLASS "EVP_PKEY*" #define X509_NAME_CLASS "X509_NAME*" @@ -581,6 +604,173 @@ static void *compat_EVP_PKEY_get0(EVP_PKEY *key) { #endif +struct ex_state { + lua_State *mainthread; + LIST_HEAD(, ex_data) data; +}; /* struct ex_state */ + +struct ex_data { + struct ex_state *state; + int refs; + int arg[4]; + LIST_ENTRY(ex_data) le; +}; /* struct ex_data */ + +enum { + EX_SSL_CTX_ALPN_SELECT_CB, +}; + +static struct ex_type { + int class_index; + int index; + void *(*get_ex_data)(); + int (*set_ex_data)(); +} ex_type[] = { + [EX_SSL_CTX_ALPN_SELECT_CB] = { CRYPTO_EX_INDEX_SSL_CTX, -1, &SSL_CTX_get_ex_data, &SSL_CTX_set_ex_data }, +}; + +static int ex_data_dup(CRYPTO_EX_DATA *to NOTUSED, CRYPTO_EX_DATA *from NOTUSED, void *from_d, int idx NOTUSED, long argl NOTUSED, void *argp NOTUSED) { + struct ex_data **data = from_d; + + if (*data) + (*data)->refs++; + + return 1; +} /* ex_data_dup() */ + +static void ex_data_free(void *parent NOTUSED, void *_data, CRYPTO_EX_DATA *ad NOTUSED, int idx NOTUSED, long argl NOTUSED, void *argp NOTUSED) { + struct ex_data *data = _data; + + if (!data || --data->refs > 0) + return; + + if (data->state) + LIST_REMOVE(data, le); + + free(data); +} /* ex_data_free() */ + +static int ex_initonce(void) { + struct ex_type *type; + + for (type = ex_type; type < endof(ex_type); type++) { + if (-1 == (type->index = CRYPTO_get_ex_new_index(type->class_index, 0, NULL, NULL, &ex_data_dup, &ex_data_free))) + return -1; + }; + + return 0; +} /* ex_initonce() */ + +static int ex__gc(lua_State *L) { + struct ex_state *state = lua_touserdata(L, 1); + struct ex_data *data; + + if (!state) + return 0; + + /* invalidate back references to Lua state */ + for (data = LIST_FIRST(&state->data); data; data = LIST_NEXT(data, le)) { + data->state = NULL; + } + + return 0; +} /* ex__gc() */ + +static void ex_init(lua_State *L) { + struct ex_state *state; + struct lua_State *thr; + + state = prepudata(L, sizeof *state, NULL, &ex__gc); + LIST_INIT(&state->data); + +#if defined LUA_RIDX_MAINTHREAD + lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD); + state->mainthread = lua_tothread(L, -1); + lua_pop(L, 1); +#else + lua_pushvalue(L, -1); + thr = lua_newthread(L); + lua_settable(L, LUA_REGISTRYINDEX); + state->mainthread = thr; +#endif + + lua_pushcfunction(L, &ex__gc); + lua_pushvalue(L, -2); + lua_settable(L, LUA_REGISTRYINDEX); + + lua_pop(L, 1); +} /* ex_init() */ + +static struct ex_state *ex_get(lua_State *L) { + struct ex_state *state; + + lua_pushcfunction(L, &ex__gc); + lua_gettable(L, LUA_REGISTRYINDEX); + + luaL_checktype(L, -1, LUA_TUSERDATA); + state = lua_touserdata(L, -1); + lua_pop(L, 1); + + return state; +} /* ex_get() */ + +static int ex_data_get(lua_State **L, int _type, void *obj) { + struct ex_type *type = &ex_type[_type]; + struct ex_data *data; + int i; + + if (!(data = type->get_ex_data(obj, type->index))) + return 0; + if (!data->state) + return 0; + + if (!*L) + *L = data->state->mainthread; + + for (i = 0; i < (int)countof(data->arg); i++) { + lua_rawgeti(*L, LUA_REGISTRYINDEX, data->arg[i]); + } + + return i; +} /* ex_data_get() */ + +static int ex_data_set(lua_State *L, int _type, void *obj, int n) { + struct ex_type *type = &ex_type[_type]; + struct ex_state *state; + struct ex_data *data; + int i, j; + + if ((data = type->get_ex_data(obj, type->index)) && data->state) { + for (i = 0; i < (int)countof(data->arg); i++) { + luaL_unref(L, LUA_REGISTRYINDEX, data->arg[i]); + data->arg[i] = LUA_NOREF; + } + } else { + state = ex_get(L); + + if (!(data = malloc(sizeof *data))) + return errno; + + if (!type->set_ex_data(obj, type->index, data)) + return -1; + + data->state = state; + data->refs = 1; + for (i = 0; i < (int)countof(data->arg); i++) + data->arg[i] = LUA_NOREF; + LIST_INSERT_HEAD(&state->data, data, le); + } + + for (i = n, j = 0; i > 0 && j < (int)countof(data->arg); i--, j++) { + lua_pushvalue(L, -i); + data->arg[j] = luaL_ref(L, LUA_REGISTRYINDEX); + } + + lua_pop(L, n); + + return 0; +} /* ex_data_set() */ + static void initall(lua_State *L); @@ -4609,6 +4799,32 @@ static int sx_setAlpnProtos(lua_State *L) { } /* sx_setAlpnProtos() */ #endif +#if HAVE_SSL_CTX_SET_ALPN_SELECT_CB +static int sx_setAlpnSelect_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg) { + lua_State *L = NULL; + int n; + + n = ex_data_get(&L, EX_SSL_CTX_ALPN_SELECT_CB, ssl); + + return 0; +} /* sx_setAlpnSelect_cb() */ + +static int sx_setAlpnSelect(lua_State *L) { + SSL_CTX *ctx = checksimple(L, 1, SSL_CTX_CLASS); + struct ex_data *data; + int error; + + luaL_checktype(L, 2, LUA_TFUNCTION); + error = ex_data_set(L, EX_SSL_CTX_ALPN_SELECT_CB, ctx, 1); + + SSL_CTX_set_alpn_select_cb(ctx, &sx_setAlpnSelect_cb, NULL); + + lua_pushboolean(L, 1); + + return 1; +} /* sx_setAlpnSelect() */ +#endif + static int sx__gc(lua_State *L) { SSL_CTX **ud = luaL_checkudata(L, 1, SSL_CTX_CLASS); @@ -5803,7 +6019,7 @@ int luaopen__openssl_des(lua_State *L) { * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef HAVE_DLADDR -#define HAVE_DLADDR (!defined _AIX) +#define HAVE_DLADDR (!defined _AIX) /* TODO: https://root.cern.ch/drupal/content/aix-and-dladdr */ #endif static struct { @@ -5847,8 +6063,8 @@ static unsigned long mt_gettid(void) { return _lwp_self(); #else /* - * pthread_t is an integer on Solaris and Linux, and a unique pointer - * on OpenBSD. + * pthread_t is an integer on Solaris and Linux, an unsigned integer + * on AIX, and a unique pointer on OpenBSD. */ return (unsigned long)pthread_self(); #endif @@ -5940,10 +6156,20 @@ static void initall(lua_State *L) { * already been configured. */ OPENSSL_config(NULL); + + if ((error = ex_initonce())) { + if (error == -1) { + throwssl(L, "openssl.init"); + } else { + luaL_error(L, "openssl.init: %s", xstrerror(error)); + } + } } pthread_mutex_unlock(&mutex); + ex_init(L); + addclass(L, BIGNUM_CLASS, bn_methods, bn_metatable); addclass(L, PKEY_CLASS, pk_methods, pk_metatable); addclass(L, X509_NAME_CLASS, xn_methods, xn_metatable); |