diff --git a/include/haproxy/hlua-t.h b/include/haproxy/hlua-t.h index 6e2ac8b6a..82204fe38 100644 --- a/include/haproxy/hlua-t.h +++ b/include/haproxy/hlua-t.h @@ -52,6 +52,7 @@ #define CLASS_LISTENER "Listener" #define CLASS_REGEX "Regex" #define CLASS_STKTABLE "StickTable" +#define CLASS_CERTCACHE "CertCache" struct stream; diff --git a/src/hlua.c b/src/hlua.c index cf58e9288..b925c2496 100644 --- a/src/hlua.c +++ b/src/hlua.c @@ -53,6 +53,8 @@ #include #include #include +#include +#include #include #include #include @@ -11368,6 +11370,187 @@ static struct cfg_kw_list cfg_kws = {{ },{ INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws); +/* + * This function replace a ckch_store by another one, and rebuild the ckch_inst and all its dependencies. + * It does the sam as "cli_io_handler_commit_cert" but for lua, the major + * difference is that the yield in lua and for the CLI is not handled the same + * way. + */ +__LJMP static int hlua_ckch_commit_yield(lua_State *L, int status, lua_KContext ctx) +{ + struct ckch_inst **lua_ckchi = lua_touserdata(L, -1); + struct ckch_store **lua_ckchs = lua_touserdata(L, -2); + struct ckch_inst *ckchi = *lua_ckchi; + struct ckch_store *old_ckchs = lua_ckchs[0]; + struct ckch_store *new_ckchs = lua_ckchs[1]; + struct hlua *hlua; + char *err = NULL; + int y = 1; + + hlua = hlua_gethlua(L); + + /* get the first ckchi to copy */ + if (ckchi == NULL) + ckchi = LIST_ELEM(old_ckchs->ckch_inst.n, typeof(ckchi), by_ckchs); + + /* walk through the old ckch_inst and creates new ckch_inst using the updated ckchs */ + list_for_each_entry_from(ckchi, &old_ckchs->ckch_inst, by_ckchs) { + struct ckch_inst *new_inst; + + /* it takes a lot of CPU to creates SSL_CTXs, so we yield every 10 CKCH instances */ + if (y % 10 == 0) { + + *lua_ckchi = ckchi; + + task_wakeup(hlua->task, TASK_WOKEN_MSG); + MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_ckch_commit_yield, TICK_ETERNITY, 0)); + } + + if (ckch_inst_rebuild(new_ckchs, ckchi, &new_inst, &err)) + goto error; + + /* link the new ckch_inst to the duplicate */ + LIST_APPEND(&new_ckchs->ckch_inst, &new_inst->by_ckchs); + y++; + } + + /* The generation is finished, we can insert everything */ + ckch_store_replace(old_ckchs, new_ckchs); + + lua_pop(L, 2); /* pop the lua_ckchs and ckchi */ + + HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock); + + return 0; + +error: + ckch_store_free(new_ckchs); + HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock); + WILL_LJMP(luaL_error(L, "%s", err)); + free(err); + + return 0; +} + +/* + * Replace a ckch_store in the ckchs_tree with a ckch_store created + * from the table in parameter. + * + * This is equivalent to "set ssl cert" + "commit ssl cert" over the CLI, which + * means it does not need to have a transaction since everything is done in the + * same function. + * + * CertCache.set{filename="", crt="", key="", sctl="", ocsp="", issuer=""} + * + */ +__LJMP static int hlua_ckch_set(lua_State *L) +{ + struct hlua *hlua; + struct ckch_inst **lua_ckchi; + struct ckch_store **lua_ckchs; + struct ckch_store *old_ckchs = NULL; + struct ckch_store *new_ckchs = NULL; + int errcode = 0; + char *err = NULL; + struct cert_exts *cert_ext = NULL; + char *filename; + struct cert_key_and_chain *ckch; + int ret; + + if (lua_type(L, -1) != LUA_TTABLE) + WILL_LJMP(luaL_error(L, "'CertCache.set' needs a table as argument")); + + hlua = hlua_gethlua(L); + + /* FIXME: this should not return an error but should come back later */ + if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock)) + WILL_LJMP(luaL_error(L, "CertCache already under lock")); + + ret = lua_getfield(L, -1, "filename"); + if (ret != LUA_TSTRING) { + memprintf(&err, "%sNo filename specified!\n", err ? err : ""); + errcode |= ERR_ALERT | ERR_FATAL; + goto end; + } + filename = (char *)lua_tostring(L, -1); + + + /* look for the filename in the tree */ + old_ckchs = ckchs_lookup(filename); + if (!old_ckchs) { + memprintf(&err, "%sCan't replace a certificate which is not referenced by the configuration!\n", err ? err : ""); + errcode |= ERR_ALERT | ERR_FATAL; + goto end; + } + /* TODO: handle extra_files_noext */ + + new_ckchs = ckchs_dup(old_ckchs); + if (!new_ckchs) { + memprintf(&err, "%sCannot allocate memory!\n", err ? err : ""); + errcode |= ERR_ALERT | ERR_FATAL; + goto end; + } + + ckch = new_ckchs->ckch; + + /* loop on the field in the table, which have the same name as the + * possible extensions of files */ + lua_pushnil(L); + while (lua_next(L, 1)) { + int i; + const char *field = lua_tostring(L, -2); + char *payload = (char *)lua_tostring(L, -1); + + if (!field || strcmp(field, "filename") == 0) { + lua_pop(L, 1); + continue; + } + + for (i = 0; field && cert_exts[i].ext != NULL; i++) { + if (strcmp(field, cert_exts[i].ext) == 0) { + cert_ext = &cert_exts[i]; + break; + } + } + + /* this is the default type, the field is not supported */ + if (cert_ext == NULL) { + memprintf(&err, "%sUnsupported field '%s'\n", err ? err : "", field); + errcode |= ERR_ALERT | ERR_FATAL; + goto end; + } + + /* appply the change on the duplicate */ + if (cert_exts->load(filename, payload, ckch, &err) != 0) { + memprintf(&err, "%sCan't load the payload\n", err ? err : ""); + errcode |= ERR_ALERT | ERR_FATAL; + goto end; + } + lua_pop(L, 1); + } + + /* store the pointers on the lua stack */ + lua_ckchs = lua_newuserdata(L, sizeof(struct ckch_store *) * 2); + lua_ckchs[0] = old_ckchs; + lua_ckchs[1] = new_ckchs; + lua_ckchi = lua_newuserdata(L, sizeof(struct ckch_inst *)); + *lua_ckchi = NULL; + + task_wakeup(hlua->task, TASK_WOKEN_MSG); + MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_ckch_commit_yield, TICK_ETERNITY, 0)); + +end: + HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock); + + if (errcode & ERR_CODE) { + ckch_store_free(new_ckchs); + WILL_LJMP(luaL_error(L, "%s", err)); + } + free(err); + + return 0; +} + /* This function can fail with an abort() due to an Lua critical error. * We are in the initialisation process of HAProxy, this abort() is * tolerated. @@ -11842,6 +12025,18 @@ lua_State *hlua_init_state(int thread_num) /* Set a name to the table. */ lua_setglobal(L, "Map"); + /* + * + * Register "CertCache" class + * + */ + + /* Create and fill the metatable. */ + lua_newtable(L); + /* Register */ + hlua_class_function(L, "set", hlua_ckch_set); + lua_setglobal(L, CLASS_CERTCACHE); /* Create global object called Regex */ + /* * * Register class Channel diff --git a/src/ssl_ckch.c b/src/ssl_ckch.c index 01bda1e80..692070646 100644 --- a/src/ssl_ckch.c +++ b/src/ssl_ckch.c @@ -1862,6 +1862,8 @@ void ckch_store_replace(struct ckch_store *old_ckchs, struct ckch_store *new_ckc /* * This function tries to create the new ckch_inst and their SNIs + * + * /!\ don't forget to update __hlua_ckch_commit() if you changes things there. /!\ */ static int cli_io_handler_commit_cert(struct appctx *appctx) {