about summary refs log tree commit diff stats
path: root/src/luasec/context.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/luasec/context.c')
-rw-r--r--src/luasec/context.c934
1 files changed, 934 insertions, 0 deletions
diff --git a/src/luasec/context.c b/src/luasec/context.c
new file mode 100644
index 0000000..5496fbf
--- /dev/null
+++ b/src/luasec/context.c
@@ -0,0 +1,934 @@
+/*--------------------------------------------------------------------------
+ * LuaSec 1.0.2
+ *
+ * Copyright (C) 2014-2021 Kim Alvefur, Paul Aurich, Tobias Markmann, 
+ *                         Matthew Wild.
+ * Copyright (C) 2006-2021 Bruno Silvestre.
+ *
+ *--------------------------------------------------------------------------*/
+
+#include <string.h>
+
+#if defined(WIN32)
+#include <windows.h>
+#endif
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/dh.h>
+
+#include "../lua.h"
+#include "../lauxlib.h"
+
+#include "compat.h"
+#include "context.h"
+#include "options.h"
+
+#ifndef OPENSSL_NO_EC
+#include <openssl/ec.h>
+#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)luaL_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;
+    }
+    /* fallback */
+  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);
+    /* fallback */
+  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;
+}
+
+/*-- Compat - Lua 5.1 --*/
+#if (LUA_VERSION_NUM == 501)
+
+void *lsec_testudata (lua_State *L, int ud, const char *tname) {
+  void *p = lua_touserdata(L, ud);
+  if (p != NULL) {  /* value is a userdata? */
+    if (lua_getmetatable(L, ud)) {  /* does it have a metatable? */
+      luaL_getmetatable(L, tname);  /* get correct metatable */
+      if (!lua_rawequal(L, -1, -2))  /* not the same? */
+        p = NULL;  /* value is a userdata with wrong metatable */
+      lua_pop(L, 2);  /* remove both metatables */
+      return p;
+    }
+  }
+  return NULL;  /* value is not a userdata with a metatable */
+}
+
+#endif
+
+/*------------------------------ 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;
+}