diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/openssl.c | 187 |
1 files changed, 148 insertions, 39 deletions
diff --git a/src/openssl.c b/src/openssl.c index 4e0b898..3465922 100644 --- a/src/openssl.c +++ b/src/openssl.c @@ -424,6 +424,24 @@ static void checkprotos(luaL_Buffer *B, lua_State *L, int index) { } } /* checkprotos() */ +static void pushprotos(lua_State *L, const unsigned char *p, size_t n) { + const unsigned char *pe = &p[n]; + int i = 0; + + lua_newtable(L); + + while (p < pe) { + n = *p++; + + if ((size_t)(pe - p) < n) + luaL_error(L, "corrupt ALPN protocol list (%zu > %zu)", n, (size_t)(pe - p)); + + lua_pushlstring(L, (const void *)p, n); + lua_rawseti(L, -2, ++i); + p += n; + } +} /* pushprotos() */ + static _Bool getfield(lua_State *L, int index, const char *k) { lua_getfield(L, index, k); @@ -604,15 +622,44 @@ static void *compat_EVP_PKEY_get0(EVP_PKEY *key) { #endif +typedef int auxref_t; +typedef int auxtype_t; + +static void auxL_unref(lua_State *L, auxref_t *ref) { + luaL_unref(L, LUA_REGISTRYINDEX, *ref); + *ref = LUA_NOREF; +} /* auxL_unref() */ + +static void auxL_ref(lua_State *L, int index, auxref_t *ref) { + auxL_unref(L, ref); + lua_pushvalue(L, index); + *ref = luaL_ref(L, LUA_REGISTRYINDEX); +} /* auxL_ref() */ + +static auxtype_t auxL_getref(lua_State *L, auxref_t ref) { + if (ref == LUA_NOREF || ref == LUA_REFNIL) { + lua_pushnil(L); + } else { + lua_rawgeti(L, LUA_REGISTRYINDEX, ref); + } + + return lua_type(L, -1); +} /* auxL_getref() */ + + struct ex_state { - lua_State *mainthread; + lua_State *L; LIST_HEAD(, ex_data) data; }; /* struct ex_state */ +#ifndef EX_DATA_MAXARGS +#define EX_DATA_MAXARGS 4 +#endif + struct ex_data { struct ex_state *state; int refs; - int arg[4]; + auxref_t arg[EX_DATA_MAXARGS]; LIST_ENTRY(ex_data) le; }; /* struct ex_data */ @@ -621,40 +668,47 @@ enum { }; static struct ex_type { - int class_index; - int index; + int class_index; /* OpenSSL object type identifier */ + int index; /* OpenSSL-allocated external data identifier */ 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) { +static int ex_ondup(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() */ +} /* ex_ondup() */ -static void ex_data_free(void *parent NOTUSED, void *_data, CRYPTO_EX_DATA *ad NOTUSED, int idx NOTUSED, long argl NOTUSED, void *argp NOTUSED) { +static void ex_onfree(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) + if (data->state) { + int i; + + for (i = 0; i < (int)countof(data->arg); i++) { + auxL_unref(data->state->L, &data->arg[i]); + } + LIST_REMOVE(data, le); + } free(data); -} /* ex_data_free() */ +} /* ex_onfree() */ 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))) + if (-1 == (type->index = CRYPTO_get_ex_new_index(type->class_index, 0, NULL, NULL, &ex_ondup, &ex_onfree))) return -1; }; @@ -676,22 +730,27 @@ static int ex__gc(lua_State *L) { return 0; } /* ex__gc() */ -static void ex_init(lua_State *L) { +static void ex_newstate(lua_State *L) { struct ex_state *state; struct lua_State *thr; state = prepudata(L, sizeof *state, NULL, &ex__gc); LIST_INIT(&state->data); + /* + * XXX: Don't reuse mainthread because if an error occurs in a + * callback Lua might longjmp across the OpenSSL call stack. + * Instead, we'll install our own panic handlers. + */ #if defined LUA_RIDX_MAINTHREAD lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD); - state->mainthread = lua_tothread(L, -1); + state->L = 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; + state->L = thr; #endif lua_pushcfunction(L, &ex__gc); @@ -699,9 +758,9 @@ static void ex_init(lua_State *L) { lua_settable(L, LUA_REGISTRYINDEX); lua_pop(L, 1); -} /* ex_init() */ +} /* ex_newstate() */ -static struct ex_state *ex_get(lua_State *L) { +static struct ex_state *ex_getstate(lua_State *L) { struct ex_state *state; lua_pushcfunction(L, &ex__gc); @@ -712,12 +771,12 @@ static struct ex_state *ex_get(lua_State *L) { lua_pop(L, 1); return state; -} /* ex_get() */ +} /* ex_getstate() */ -static int ex_data_get(lua_State **L, int _type, void *obj) { +static size_t ex_getdata(lua_State **L, int _type, void *obj) { struct ex_type *type = &ex_type[_type]; struct ex_data *data; - int i; + size_t i; if (!(data = type->get_ex_data(obj, type->index))) return 0; @@ -725,28 +784,31 @@ static int ex_data_get(lua_State **L, int _type, void *obj) { return 0; if (!*L) - *L = data->state->mainthread; + *L = data->state->L; + + if (!lua_checkstack(*L, countof(data->arg))) + return 0; - for (i = 0; i < (int)countof(data->arg); i++) { + for (i = 0; i < countof(data->arg) && data->arg[i] != LUA_NOREF; i++) { lua_rawgeti(*L, LUA_REGISTRYINDEX, data->arg[i]); } return i; -} /* ex_data_get() */ +} /* ex_getdata() */ -static int ex_data_set(lua_State *L, int _type, void *obj, int n) { +/* returns 0 on success, otherwise error (>0 == errno, -1 == OpenSSL error) */ +static int ex_setdata(lua_State *L, int _type, void *obj, size_t n) { struct ex_type *type = &ex_type[_type]; struct ex_state *state; struct ex_data *data; - int i, j; + size_t 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; + for (i = 0; i < countof(data->arg); i++) { + auxL_unref(L, &data->arg[i]); } } else { - state = ex_get(L); + state = ex_getstate(L); if (!(data = malloc(sizeof *data))) return errno; @@ -756,20 +818,19 @@ static int ex_data_set(lua_State *L, int _type, void *obj, int n) { data->state = state; data->refs = 1; - for (i = 0; i < (int)countof(data->arg); i++) + for (i = 0; i < 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); + for (i = n, j = 0; i > 0 && j < countof(data->arg); i--, j++) { + auxL_ref(L, -(int)i, &data->arg[j]); } lua_pop(L, n); return 0; -} /* ex_data_set() */ +} /* ex_setdata() */ static void initall(lua_State *L); @@ -4800,13 +4861,44 @@ static int sx_setAlpnProtos(lua_State *L) { #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) { +static SSL *ssl_push(lua_State *, SSL *); + +static int sx_setAlpnSelect_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *_ctx) { + SSL_CTX *ctx = _ctx; lua_State *L = NULL; - int n; + size_t n; + int top, status; - n = ex_data_get(&L, EX_SSL_CTX_ALPN_SELECT_CB, ssl); + if (0 == (n = ex_getdata(&L, EX_SSL_CTX_ALPN_SELECT_CB, ctx))) + return SSL_TLSEXT_ERR_ALERT_FATAL; - return 0; + top = lua_gettop(L) - n; + + /* TODO: Install temporary panic handler to catch OOM errors */ + + /* pass the SSL object as first argument */ + ssl_push(L, ssl); + pushprotos(L, in, inlen); + + /* TODO: lua_rotate ssl and protocols table into position. */ + + if (LUA_OK != (status = lua_pcall(L, 2 + (n - 1), 1, 0))) + goto fatal; + + /* TODO: check return value */ + (void)out; (void)outlen; + + lua_settop(L, top); + + return SSL_TLSEXT_ERR_OK; +fatal: + lua_settop(L, top); + + return SSL_TLSEXT_ERR_ALERT_FATAL; +noack: + lua_settop(L, top); + + return SSL_TLSEXT_ERR_NOACK; } /* sx_setAlpnSelect_cb() */ static int sx_setAlpnSelect(lua_State *L) { @@ -4815,9 +4907,17 @@ static int sx_setAlpnSelect(lua_State *L) { int error; luaL_checktype(L, 2, LUA_TFUNCTION); - error = ex_data_set(L, EX_SSL_CTX_ALPN_SELECT_CB, ctx, 1); + if ((error = ex_setdata(L, EX_SSL_CTX_ALPN_SELECT_CB, ctx, 1))) { + if (error > 0) { + return luaL_error(L, "unable to set ALPN protocol selection callback: %s", xstrerror(error)); + } else if (!ERR_peek_error()) { + return luaL_error(L, "unable to set ALPN protocol selection callback: Unknown internal error"); + } else { + return throwssl(L, "ssl.context:setAlpnSelect"); + } + } - SSL_CTX_set_alpn_select_cb(ctx, &sx_setAlpnSelect_cb, NULL); + SSL_CTX_set_alpn_select_cb(ctx, &sx_setAlpnSelect_cb, ctx); lua_pushboolean(L, 1); @@ -4936,6 +5036,15 @@ int luaopen__openssl_ssl_context(lua_State *L) { * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +static SSL *ssl_push(lua_State *L, SSL *ssl) { + SSL **ud = prepsimple(L, SSL_CLASS); + + CRYPTO_add(&(ssl)->references, 1, CRYPTO_LOCK_SSL); + *ud = ssl; + + return *ud; +} /* ssl_push() */ + static int ssl_new(lua_State *L) { lua_pushnil(L); @@ -6168,7 +6277,7 @@ static void initall(lua_State *L) { pthread_mutex_unlock(&mutex); - ex_init(L); + ex_newstate(L); addclass(L, BIGNUM_CLASS, bn_methods, bn_metatable); addclass(L, PKEY_CLASS, pk_methods, pk_metatable); |