/*-------------------------------------------------------------------------- * LuaSec 1.0.2 * * Copyright (C) 2014-2021 Kim Alvefur, Paul Aurich, Tobias Markmann, * Matthew Wild. * Copyright (C) 2006-2021 Bruno Silvestre. * *--------------------------------------------------------------------------*/ #include #if defined(WIN32) #include #endif #include #include #include #include #include #include "../lua.h" #include "../lauxlib.h" #include "compat.h" #include "context.h" #include "options.h" #ifndef OPENSSL_NO_EC #include #include "ec.h" #endif /*--------------------------- Auxiliary Functions ----------------------------*/ /** * Return the context. */ static p_context checkctx(lua_State *L, int idx) { return (p_context)luaL_checkudata(L, idx, "SSL:Context"); } static p_context testctx(lua_State *L, int idx) { return (p_context)luasocket_testudata(L, idx, "SSL:Context"); } /** * Prepare the SSL options flag. */ static int set_option_flag(const char *opt, unsigned long *flag) { lsec_ssl_option_t *p; for (p = lsec_get_ssl_options(); p->name; p++) { if (!strcmp(opt, p->name)) { *flag |= p->code; return 1; } } return 0; } #ifndef LSEC_API_OPENSSL_1_1_0 /** * Find the protocol. */ static const SSL_METHOD* str2method(const char *method, int *vmin, int *vmax) { (void)vmin; (void)vmax; if (!strcmp(method, "any")) return SSLv23_method(); if (!strcmp(method, "sslv23")) return SSLv23_method(); // deprecated if (!strcmp(method, "tlsv1")) return TLSv1_method(); if (!strcmp(method, "tlsv1_1")) return TLSv1_1_method(); if (!strcmp(method, "tlsv1_2")) return TLSv1_2_method(); return NULL; } #else /** * Find the protocol. */ static const SSL_METHOD* str2method(const char *method, int *vmin, int *vmax) { if (!strcmp(method, "any") || !strcmp(method, "sslv23")) { // 'sslv23' is deprecated *vmin = 0; *vmax = 0; return TLS_method(); } else if (!strcmp(method, "tlsv1")) { *vmin = TLS1_VERSION; *vmax = TLS1_VERSION; return TLS_method(); } else if (!strcmp(method, "tlsv1_1")) { *vmin = TLS1_1_VERSION; *vmax = TLS1_1_VERSION; return TLS_method(); } else if (!strcmp(method, "tlsv1_2")) { *vmin = TLS1_2_VERSION; *vmax = TLS1_2_VERSION; return TLS_method(); } #if defined(TLS1_3_VERSION) else if (!strcmp(method, "tlsv1_3")) { *vmin = TLS1_3_VERSION; *vmax = TLS1_3_VERSION; return TLS_method(); } #endif return NULL; } #endif /** * Prepare the SSL handshake verify flag. */ static int set_verify_flag(const char *str, int *flag) { if (!strcmp(str, "none")) { *flag |= SSL_VERIFY_NONE; return 1; } if (!strcmp(str, "peer")) { *flag |= SSL_VERIFY_PEER; return 1; } if (!strcmp(str, "client_once")) { *flag |= SSL_VERIFY_CLIENT_ONCE; return 1; } if (!strcmp(str, "fail_if_no_peer_cert")) { *flag |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; return 1; } return 0; } /** * Password callback for reading the private key. */ static int passwd_cb(char *buf, int size, int flag, void *udata) { lua_State *L = (lua_State*)udata; switch (lua_type(L, 3)) { case LUA_TFUNCTION: lua_pushvalue(L, 3); lua_call(L, 0, 1); if (lua_type(L, -1) != LUA_TSTRING) { lua_pop(L, 1); /* Remove the result from the stack */ return 0; } /* fall through */ case LUA_TSTRING: strncpy(buf, lua_tostring(L, -1), size); lua_pop(L, 1); /* Remove the result from the stack */ buf[size-1] = '\0'; return (int)strlen(buf); } return 0; } /** * Add an error related to a depth certificate of the chain. */ static void add_cert_error(lua_State *L, SSL *ssl, int err, int depth) { luaL_getmetatable(L, "SSL:Verify:Registry"); lua_pushlightuserdata(L, (void*)ssl); lua_gettable(L, -2); if (lua_isnil(L, -1)) { lua_pop(L, 1); /* Create an error table for this connection */ lua_newtable(L); lua_pushlightuserdata(L, (void*)ssl); lua_pushvalue(L, -2); /* keep the table on stack */ lua_settable(L, -4); } lua_rawgeti(L, -1, depth+1); /* If the table doesn't exist, create it */ if (lua_isnil(L, -1)) { lua_pop(L, 1); /* remove 'nil' from stack */ lua_newtable(L); lua_pushvalue(L, -1); /* keep the table on stack */ lua_rawseti(L, -3, depth+1); } lua_pushstring(L, X509_verify_cert_error_string(err)); lua_rawseti(L, -2, lua_rawlen(L, -2) + 1); /* Clear the stack */ lua_pop(L, 3); } /** * Call Lua user function to get the DH key. */ static DH *dhparam_cb(SSL *ssl, int is_export, int keylength) { BIO *bio; lua_State *L; SSL_CTX *ctx = SSL_get_SSL_CTX(ssl); p_context pctx = (p_context)SSL_CTX_get_app_data(ctx); L = pctx->L; /* Get the callback */ luaL_getmetatable(L, "SSL:DH:Registry"); lua_pushlightuserdata(L, (void*)ctx); lua_gettable(L, -2); /* Invoke the callback */ lua_pushboolean(L, is_export); lua_pushnumber(L, keylength); lua_call(L, 2, 1); /* Load parameters from returned value */ if (lua_type(L, -1) != LUA_TSTRING) { lua_pop(L, 2); /* Remove values from stack */ return NULL; } bio = BIO_new_mem_buf((void*)lua_tostring(L, -1), lua_rawlen(L, -1)); if (bio) { pctx->dh_param = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); BIO_free(bio); } lua_pop(L, 2); /* Remove values from stack */ return pctx->dh_param; } /** * Set the "ignore purpose" before to start verifing the certificate chain. */ static int cert_verify_cb(X509_STORE_CTX *x509_ctx, void *ptr) { int verify; lua_State *L; SSL_CTX *ctx = (SSL_CTX*)ptr; p_context pctx = (p_context)SSL_CTX_get_app_data(ctx); L = pctx->L; /* Get verify flags */ luaL_getmetatable(L, "SSL:Verify:Registry"); lua_pushlightuserdata(L, (void*)ctx); lua_gettable(L, -2); verify = (int)lua_tonumber(L, -1); lua_pop(L, 2); /* Remove values from stack */ if (verify & LSEC_VERIFY_IGNORE_PURPOSE) { /* Set parameters to ignore the server purpose */ X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(x509_ctx); if (param) { X509_VERIFY_PARAM_set_purpose(param, X509_PURPOSE_SSL_SERVER); X509_VERIFY_PARAM_set_trust(param, X509_TRUST_SSL_SERVER); } } /* Call OpenSSL standard verification function */ return X509_verify_cert(x509_ctx); } /** * This callback implements the "continue on error" flag and log the errors. */ static int verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) { int err; int verify; SSL *ssl; SSL_CTX *ctx; p_context pctx; lua_State *L; /* Short-circuit optimization */ if (preverify_ok) return 1; ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); ctx = SSL_get_SSL_CTX(ssl); pctx = (p_context)SSL_CTX_get_app_data(ctx); L = pctx->L; /* Get verify flags */ luaL_getmetatable(L, "SSL:Verify:Registry"); lua_pushlightuserdata(L, (void*)ctx); lua_gettable(L, -2); verify = (int)lua_tonumber(L, -1); lua_pop(L, 2); /* Remove values from stack */ err = X509_STORE_CTX_get_error(x509_ctx); if (err != X509_V_OK) add_cert_error(L, ssl, err, X509_STORE_CTX_get_error_depth(x509_ctx)); return (verify & LSEC_VERIFY_CONTINUE ? 1 : preverify_ok); } /*------------------------------ Lua Functions -------------------------------*/ /** * Create a SSL context. */ static int create(lua_State *L) { p_context ctx; const char *str_method; const SSL_METHOD *method; int vmin, vmax; str_method = luaL_checkstring(L, 1); method = str2method(str_method, &vmin, &vmax); if (!method) { lua_pushnil(L); lua_pushfstring(L, "invalid protocol (%s)", str_method); return 2; } ctx = (p_context) lua_newuserdata(L, sizeof(t_context)); if (!ctx) { lua_pushnil(L); lua_pushstring(L, "error creating context"); return 2; } memset(ctx, 0, sizeof(t_context)); ctx->context = SSL_CTX_new(method); if (!ctx->context) { lua_pushnil(L); lua_pushfstring(L, "error creating context (%s)", ERR_reason_error_string(ERR_get_error())); return 2; } #ifdef LSEC_API_OPENSSL_1_1_0 SSL_CTX_set_min_proto_version(ctx->context, vmin); SSL_CTX_set_max_proto_version(ctx->context, vmax); #endif ctx->mode = LSEC_MODE_INVALID; ctx->L = L; luaL_getmetatable(L, "SSL:Context"); lua_setmetatable(L, -2); /* No session support */ SSL_CTX_set_session_cache_mode(ctx->context, SSL_SESS_CACHE_OFF); /* Link LuaSec context with the OpenSSL context */ SSL_CTX_set_app_data(ctx->context, ctx); return 1; } /** * Load the trusting certificates. */ static int load_locations(lua_State *L) { SSL_CTX *ctx = lsec_checkcontext(L, 1); const char *cafile = luaL_optstring(L, 2, NULL); const char *capath = luaL_optstring(L, 3, NULL); if (SSL_CTX_load_verify_locations(ctx, cafile, capath) != 1) { lua_pushboolean(L, 0); lua_pushfstring(L, "error loading CA locations (%s)", ERR_reason_error_string(ERR_get_error())); return 2; } lua_pushboolean(L, 1); return 1; } /** * Load the certificate file. */ static int load_cert(lua_State *L) { SSL_CTX *ctx = lsec_checkcontext(L, 1); const char *filename = luaL_checkstring(L, 2); if (SSL_CTX_use_certificate_chain_file(ctx, filename) != 1) { lua_pushboolean(L, 0); lua_pushfstring(L, "error loading certificate (%s)", ERR_reason_error_string(ERR_get_error())); return 2; } lua_pushboolean(L, 1); return 1; } /** * Load the key file -- only in PEM format. */ static int load_key(lua_State *L) { int ret = 1; SSL_CTX *ctx = lsec_checkcontext(L, 1); const char *filename = luaL_checkstring(L, 2); switch (lua_type(L, 3)) { case LUA_TSTRING: case LUA_TFUNCTION: SSL_CTX_set_default_passwd_cb(ctx, passwd_cb); SSL_CTX_set_default_passwd_cb_userdata(ctx, L); /* fall through */ case LUA_TNIL: if (SSL_CTX_use_PrivateKey_file(ctx, filename, SSL_FILETYPE_PEM) == 1) lua_pushboolean(L, 1); else { ret = 2; lua_pushboolean(L, 0); lua_pushfstring(L, "error loading private key (%s)", ERR_reason_error_string(ERR_get_error())); } SSL_CTX_set_default_passwd_cb(ctx, NULL); SSL_CTX_set_default_passwd_cb_userdata(ctx, NULL); break; default: lua_pushstring(L, "invalid callback value"); lua_error(L); } return ret; } /** * Check that the certificate public key matches the private key */ static int check_key(lua_State *L) { SSL_CTX *ctx = lsec_checkcontext(L, 1); lua_pushboolean(L, SSL_CTX_check_private_key(ctx)); return 1; } /** * Set the cipher list. */ static int set_cipher(lua_State *L) { SSL_CTX *ctx = lsec_checkcontext(L, 1); const char *list = luaL_checkstring(L, 2); if (SSL_CTX_set_cipher_list(ctx, list) != 1) { lua_pushboolean(L, 0); lua_pushfstring(L, "error setting cipher list (%s)", ERR_reason_error_string(ERR_get_error())); return 2; } lua_pushboolean(L, 1); return 1; } /** * Set the cipher suites. */ static int set_ciphersuites(lua_State *L) { #if defined(TLS1_3_VERSION) SSL_CTX *ctx = lsec_checkcontext(L, 1); const char *list = luaL_checkstring(L, 2); if (SSL_CTX_set_ciphersuites(ctx, list) != 1) { lua_pushboolean(L, 0); lua_pushfstring(L, "error setting cipher list (%s)", ERR_reason_error_string(ERR_get_error())); return 2; } #endif lua_pushboolean(L, 1); return 1; } /** * Set the depth for certificate checking. */ static int set_depth(lua_State *L) { SSL_CTX *ctx = lsec_checkcontext(L, 1); SSL_CTX_set_verify_depth(ctx, (int)luaL_checkinteger(L, 2)); lua_pushboolean(L, 1); return 1; } /** * Set the handshake verify options. */ static int set_verify(lua_State *L) { int i; const char *str; int flag = 0; SSL_CTX *ctx = lsec_checkcontext(L, 1); int max = lua_gettop(L); for (i = 2; i <= max; i++) { str = luaL_checkstring(L, i); if (!set_verify_flag(str, &flag)) { lua_pushboolean(L, 0); lua_pushfstring(L, "invalid verify option (%s)", str); return 2; } } if (flag) SSL_CTX_set_verify(ctx, flag, NULL); lua_pushboolean(L, 1); return 1; } /** * Set the protocol options. */ static int set_options(lua_State *L) { int i; const char *str; unsigned long flag = 0L; SSL_CTX *ctx = lsec_checkcontext(L, 1); int max = lua_gettop(L); /* any option? */ if (max > 1) { for (i = 2; i <= max; i++) { str = luaL_checkstring(L, i); if (!set_option_flag(str, &flag)) { lua_pushboolean(L, 0); lua_pushfstring(L, "invalid option (%s)", str); return 2; } } SSL_CTX_set_options(ctx, flag); } lua_pushboolean(L, 1); return 1; } /** * Set the context mode. */ static int set_mode(lua_State *L) { p_context ctx = checkctx(L, 1); const char *str = luaL_checkstring(L, 2); if (!strcmp("server", str)) { ctx->mode = LSEC_MODE_SERVER; lua_pushboolean(L, 1); return 1; } if (!strcmp("client", str)) { ctx->mode = LSEC_MODE_CLIENT; lua_pushboolean(L, 1); return 1; } lua_pushboolean(L, 0); lua_pushfstring(L, "invalid mode (%s)", str); return 1; } /** * Configure DH parameters. */ static int set_dhparam(lua_State *L) { SSL_CTX *ctx = lsec_checkcontext(L, 1); SSL_CTX_set_tmp_dh_callback(ctx, dhparam_cb); /* Save callback */ luaL_getmetatable(L, "SSL:DH:Registry"); lua_pushlightuserdata(L, (void*)ctx); lua_pushvalue(L, 2); lua_settable(L, -3); return 0; } #if !defined(OPENSSL_NO_EC) /** * Set elliptic curve. */ static int set_curve(lua_State *L) { long ret; EC_KEY *key = NULL; SSL_CTX *ctx = lsec_checkcontext(L, 1); const char *str = luaL_checkstring(L, 2); SSL_CTX_set_options(ctx, SSL_OP_SINGLE_ECDH_USE); key = lsec_find_ec_key(L, str); if (!key) { lua_pushboolean(L, 0); lua_pushfstring(L, "elliptic curve '%s' not supported", str); return 2; } ret = SSL_CTX_set_tmp_ecdh(ctx, key); /* SSL_CTX_set_tmp_ecdh takes its own reference */ EC_KEY_free(key); if (!ret) { lua_pushboolean(L, 0); lua_pushfstring(L, "error setting elliptic curve (%s)", ERR_reason_error_string(ERR_get_error())); return 2; } lua_pushboolean(L, 1); return 1; } /** * Set elliptic curves list. */ static int set_curves_list(lua_State *L) { SSL_CTX *ctx = lsec_checkcontext(L, 1); const char *str = luaL_checkstring(L, 2); SSL_CTX_set_options(ctx, SSL_OP_SINGLE_ECDH_USE); if (SSL_CTX_set1_curves_list(ctx, str) != 1) { lua_pushboolean(L, 0); lua_pushfstring(L, "unknown elliptic curve in \"%s\"", str); return 2; } #if defined(LIBRESSL_VERSION_NUMBER) || !defined(LSEC_API_OPENSSL_1_1_0) (void)SSL_CTX_set_ecdh_auto(ctx, 1); #endif lua_pushboolean(L, 1); return 1; } #endif /** * Set the protocols a client should send for ALPN. */ static int set_alpn(lua_State *L) { long ret; size_t len; p_context ctx = checkctx(L, 1); const char *str = luaL_checklstring(L, 2, &len); ret = SSL_CTX_set_alpn_protos(ctx->context, (const unsigned char*)str, len); if (ret) { lua_pushboolean(L, 0); lua_pushfstring(L, "error setting ALPN (%s)", ERR_reason_error_string(ERR_get_error())); return 2; } lua_pushboolean(L, 1); return 1; } /** * This standard callback calls the server's callback in Lua sapce. * The server has to return a list in wire-format strings. * This function uses a helper function to match server and client lists. */ static int alpn_cb(SSL *s, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg) { int ret; size_t server_len; const char *server; p_context ctx = (p_context)arg; lua_State *L = ctx->L; luaL_getmetatable(L, "SSL:ALPN:Registry"); lua_pushlightuserdata(L, (void*)ctx->context); lua_gettable(L, -2); lua_pushlstring(L, (const char*)in, inlen); lua_call(L, 1, 1); if (!lua_isstring(L, -1)) { lua_pop(L, 2); return SSL_TLSEXT_ERR_NOACK; } // Protocol list from server in wire-format string server = luaL_checklstring(L, -1, &server_len); ret = SSL_select_next_proto((unsigned char**)out, outlen, (const unsigned char*)server, server_len, in, inlen); if (ret != OPENSSL_NPN_NEGOTIATED) { lua_pop(L, 2); return SSL_TLSEXT_ERR_NOACK; } // Copy the result because lua_pop() can collect the pointer ctx->alpn = malloc(*outlen); memcpy(ctx->alpn, (void*)*out, *outlen); *out = (const unsigned char*)ctx->alpn; lua_pop(L, 2); return SSL_TLSEXT_ERR_OK; } /** * Set a callback a server can use to select the next protocol with ALPN. */ static int set_alpn_cb(lua_State *L) { p_context ctx = checkctx(L, 1); luaL_getmetatable(L, "SSL:ALPN:Registry"); lua_pushlightuserdata(L, (void*)ctx->context); lua_pushvalue(L, 2); lua_settable(L, -3); SSL_CTX_set_alpn_select_cb(ctx->context, alpn_cb, ctx); lua_pushboolean(L, 1); return 1; } #if defined(LSEC_ENABLE_DANE) /* * DANE */ static int set_dane(lua_State *L) { int ret; SSL_CTX *ctx = lsec_checkcontext(L, 1); ret = SSL_CTX_dane_enable(ctx); lua_pushboolean(L, (ret > 0)); return 1; } #endif /** * Package functions */ static luaL_Reg funcs[] = { {"create", create}, {"locations", load_locations}, {"loadcert", load_cert}, {"loadkey", load_key}, {"checkkey", check_key}, {"setalpn", set_alpn}, {"setalpncb", set_alpn_cb}, {"setcipher", set_cipher}, {"setciphersuites", set_ciphersuites}, {"setdepth", set_depth}, {"setdhparam", set_dhparam}, {"setverify", set_verify}, {"setoptions", set_options}, {"setmode", set_mode}, #if !defined(OPENSSL_NO_EC) {"setcurve", set_curve}, {"setcurveslist", set_curves_list}, #endif #if defined(LSEC_ENABLE_DANE) {"setdane", set_dane}, #endif {NULL, NULL} }; /*-------------------------------- Metamethods -------------------------------*/ /** * Collect SSL context -- GC metamethod. */ static int meth_destroy(lua_State *L) { p_context ctx = checkctx(L, 1); if (ctx->context) { /* Clear registries */ luaL_getmetatable(L, "SSL:DH:Registry"); lua_pushlightuserdata(L, (void*)ctx->context); lua_pushnil(L); lua_settable(L, -3); luaL_getmetatable(L, "SSL:Verify:Registry"); lua_pushlightuserdata(L, (void*)ctx->context); lua_pushnil(L); lua_settable(L, -3); luaL_getmetatable(L, "SSL:ALPN:Registry"); lua_pushlightuserdata(L, (void*)ctx->context); lua_pushnil(L); lua_settable(L, -3); SSL_CTX_free(ctx->context); ctx->context = NULL; } return 0; } /** * Object information -- tostring metamethod. */ static int meth_tostring(lua_State *L) { p_context ctx = checkctx(L, 1); lua_pushfstring(L, "SSL context: %p", ctx); return 1; } /** * Set extra flags for handshake verification. */ static int meth_set_verify_ext(lua_State *L) { int i; const char *str; int crl_flag = 0; int lsec_flag = 0; SSL_CTX *ctx = lsec_checkcontext(L, 1); int max = lua_gettop(L); for (i = 2; i <= max; i++) { str = luaL_checkstring(L, i); if (!strcmp(str, "lsec_continue")) { lsec_flag |= LSEC_VERIFY_CONTINUE; } else if (!strcmp(str, "lsec_ignore_purpose")) { lsec_flag |= LSEC_VERIFY_IGNORE_PURPOSE; } else if (!strcmp(str, "crl_check")) { crl_flag |= X509_V_FLAG_CRL_CHECK; } else if (!strcmp(str, "crl_check_chain")) { crl_flag |= X509_V_FLAG_CRL_CHECK_ALL; } else { lua_pushboolean(L, 0); lua_pushfstring(L, "invalid verify option (%s)", str); return 2; } } /* Set callback? */ if (lsec_flag) { SSL_CTX_set_verify(ctx, SSL_CTX_get_verify_mode(ctx), verify_cb); SSL_CTX_set_cert_verify_callback(ctx, cert_verify_cb, (void*)ctx); /* Save flag */ luaL_getmetatable(L, "SSL:Verify:Registry"); lua_pushlightuserdata(L, (void*)ctx); lua_pushnumber(L, lsec_flag); lua_settable(L, -3); } else { SSL_CTX_set_verify(ctx, SSL_CTX_get_verify_mode(ctx), NULL); SSL_CTX_set_cert_verify_callback(ctx, NULL, NULL); /* Remove flag */ luaL_getmetatable(L, "SSL:Verify:Registry"); lua_pushlightuserdata(L, (void*)ctx); lua_pushnil(L); lua_settable(L, -3); } /* X509 flag */ X509_STORE_set_flags(SSL_CTX_get_cert_store(ctx), crl_flag); /* Ok */ lua_pushboolean(L, 1); return 1; } /** * Context metamethods. */ static luaL_Reg meta[] = { {"__close", meth_destroy}, {"__gc", meth_destroy}, {"__tostring", meth_tostring}, {NULL, NULL} }; /** * Index metamethods. */ static luaL_Reg meta_index[] = { {"setverifyext", meth_set_verify_ext}, {NULL, NULL} }; /*----------------------------- Public Functions ---------------------------*/ /** * Retrieve the SSL context from the Lua stack. */ SSL_CTX* lsec_checkcontext(lua_State *L, int idx) { p_context ctx = checkctx(L, idx); return ctx->context; } SSL_CTX* lsec_testcontext(lua_State *L, int idx) { p_context ctx = testctx(L, idx); return (ctx) ? ctx->context : NULL; } /** * Retrieve the mode from the context in the Lua stack. */ int lsec_getmode(lua_State *L, int idx) { p_context ctx = checkctx(L, idx); return ctx->mode; } /*------------------------------ Initialization ------------------------------*/ /** * Registre the module. */ LSEC_API int luaopen_ssl_context(lua_State *L) { luaL_newmetatable(L, "SSL:DH:Registry"); /* Keep all DH callbacks */ luaL_newmetatable(L, "SSL:ALPN:Registry"); /* Keep all ALPN callbacks */ luaL_newmetatable(L, "SSL:Verify:Registry"); /* Keep all verify flags */ luaL_newmetatable(L, "SSL:Context"); setfuncs(L, meta); /* Create __index metamethods for context */ luaL_newlib(L, meta_index); lua_setfield(L, -2, "__index"); lsec_load_curves(L); /* Return the module */ luaL_newlib(L, funcs); lua_pushvalue(L, -1); lua_setglobal(L, "context"); return 1; }