isbear@23:
isbear@59: /* Copyright 2009-2016 Myhailo Danylenko
isbear@23:
isbear@23: This file is part of lua-lm.
isbear@23:
isbear@23: lua-lm is free software: you can redistribute it and/or modify
isbear@23: it under the terms of the GNU General Public License as published by
isbear@23: the Free Software Foundation, either version 2 of the License, or
isbear@23: (at your option) any later version.
isbear@23:
isbear@23: This program is distributed in the hope that it will be useful,
isbear@23: but WITHOUT ANY WARRANTY; without even the implied warranty of
isbear@23: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
isbear@23: GNU General Public License for more details.
isbear@23:
isbear@23: You should have received a copy of the GNU General Public License
isbear@23: along with this program. If not, see . */
isbear@0:
isbear@0: #include
isbear@0: #include
isbear@0: #include
isbear@0: #include
isbear@0: #include
isbear@0:
isbear@6: #include "config.h"
isbear@0: #include "util.h"
isbear@0: #include "lm_types.h"
isbear@0:
isbear@0: /// lm.ssl
isbear@0: /// Object, containing information about ssl abilities for connection.
isbear@0: /// Create, set parameters, and attach to connection with 'ssl' method.
isbear@0:
isbear@0: /// ssl status
isbear@0: /// String, representing what problem have current ssl session.
isbear@4: /// G:
isbear@11: const string2enum_t status_lm_ssl[] = {
isbear@0: { "no cert found", LM_SSL_STATUS_NO_CERT_FOUND },
isbear@0: { "untrusted cert", LM_SSL_STATUS_UNTRUSTED_CERT },
isbear@0: { "cert expired", LM_SSL_STATUS_CERT_EXPIRED },
isbear@0: { "cert not activated", LM_SSL_STATUS_CERT_NOT_ACTIVATED },
isbear@0: { "cert hostname mismatch", LM_SSL_STATUS_CERT_HOSTNAME_MISMATCH },
isbear@0: { "cert fingerprint mismatch", LM_SSL_STATUS_CERT_FINGERPRINT_MISMATCH },
isbear@0: { "generic error", LM_SSL_STATUS_GENERIC_ERROR },
isbear@0: { NULL, 0 }, // XXX
isbear@0: };
isbear@0:
isbear@0: /// ssl callback function
isbear@0: /// User function, called when ssl error happens.
isbear@42: /// A: userdata (lm ssl object), argument enum field (ssl status)
isbear@0: /// R: boolean (false if connection process should be terminated)
isbear@11: LmSSLResponse callback_lm_ssl (LmSSL *ssl, LmSSLStatus status, llm_callback_t *cb)
isbear@0: {
isbear@0: int ret;
isbear@0: lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->reference);
isbear@11: bless_lm_ssl (cb->L, ssl);
isbear@0: // XXX lm_ssl_unref (ssl);
isbear@11: luaL_pushenum (cb->L, status, status_lm_ssl);
isbear@42: if (lua_pcall (cb->L, 2, 1, 0)) {
isbear@9: W ("SSL callback error: %s", lua_tostring (cb->L, -1));
isbear@0: lua_pop (cb->L, 1);
isbear@0: return LM_SSL_RESPONSE_CONTINUE;
isbear@0: }
isbear@0: ret = lua_toboolean (cb->L, -1);
isbear@0: lua_pop (cb->L, 1);
isbear@0: if (ret)
isbear@0: return LM_SSL_RESPONSE_CONTINUE;
isbear@0: else
isbear@0: return LM_SSL_RESPONSE_STOP;
isbear@0: }
isbear@0:
isbear@57: #ifndef HAVE_LM_SHA256_FINGERPRINTS
isbear@0: static void string2fingerprint (const char *string, char *buffer)
isbear@0: {
isbear@0: int i;
isbear@0: for (i = 0; i < 16; i++) {
isbear@0: int h = g_ascii_xdigit_value ((char)string[i*3]);
isbear@0: int l = g_ascii_xdigit_value ((char)string[i*3+1]);
isbear@0: buffer[i] = (char) ((h >= 0 && l >= 0) ? h*16 + l : 0);
isbear@0: }
isbear@0: }
isbear@57: #endif
isbear@0:
isbear@0: /// lm.ssl.new
isbear@0: /// Creates new ssl object for use with connection.
isbear@0: /// You can specify server key fingerprint, callback function for error handling,
isbear@0: /// both, or neither. Though, fingerprint should go before callback function.
isbear@57: /// SSL fingerprint is a string like 'SHA256:ABCDEF123456...' (or
isbear@57: /// '01:23:45:67:89:AB:CD:EF:FE:DC:BA:98:76:54:32:10' for LM versions, older than 1.5.3).
isbear@0: /// A: string (optional ssl fingerprint), ssl callback function (optional)
isbear@42: /// R: userdata (lm ssl object)
isbear@11: static int new_lm_ssl (lua_State *L)
isbear@0: {
isbear@0: int args = lua_gettop (L);
isbear@0: LmSSL *ssl;
isbear@0: if (args == 0)
isbear@0: ssl = lm_ssl_new (NULL, NULL, NULL, NULL);
isbear@0: else if (args == 1 && !lua_isfunction (L, 1)) {
isbear@57: const char *fingerprint = luaL_checkstring (L, 1);
isbear@57: #ifndef HAVE_LM_SHA256_FINGERPRINTS
isbear@0: gchar buffer[16] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
isbear@0:
isbear@57: if (lua_rawlen (L, 1) > 46) {
isbear@0: string2fingerprint (fingerprint, buffer);
isbear@57: fingerprint = buffer;
isbear@57: } else
isbear@57: fingerprint = NULL;
isbear@57: #endif
isbear@57: ssl = lm_ssl_new (fingerprint, NULL, NULL, NULL);
isbear@0: } else {
isbear@0: llm_callback_t *cb;
isbear@57: const char *fingerprint = NULL;
isbear@0:
isbear@0: if (args > 1) {
isbear@57: fingerprint = luaL_checkstring (L, 1);
isbear@57: #ifndef HAVE_LM_SHA256_FINGERPRINTS
isbear@57: gchar buffer[16] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
isbear@57:
isbear@57: if (lua_rawlen (L, 1) > 46) {
isbear@0: string2fingerprint (fingerprint, buffer);
isbear@57: fingerprint = buffer;
isbear@57: } else
isbear@57: fingerprint = NULL;
isbear@57: #endif
isbear@0: luaL_argcheck (L, lua_isfunction (L, 2), 2, "function expected");
isbear@0: } else
isbear@0: luaL_argcheck (L, lua_isfunction (L, 1), 1, "function expected");
isbear@0:
isbear@0: cb = luaL_malloc (L, sizeof (llm_callback_t));
isbear@0: cb->reference = luaL_ref (L, LUA_REGISTRYINDEX);
isbear@0: cb->L = L;
isbear@0:
isbear@57: ssl = lm_ssl_new (fingerprint, (LmSSLFunction)callback_lm_ssl,
isbear@0: cb, (GDestroyNotify)llm_callback_destroy);
isbear@0: }
isbear@11: bless_lm_ssl (L, ssl);
isbear@0: lm_ssl_unref (ssl); // XXX
isbear@38: D ("SSL %p created", ssl);
isbear@0: return 1;
isbear@0: }
isbear@0:
isbear@0: /// lm.ssl.bless
isbear@0: /// Blesses given pointer to lm ssl object.
isbear@0: /// A: lightuserdata (C lm ssl object)
isbear@42: /// R: userdata (lm ssl object)
isbear@11: static int bless_lua_lm_ssl (lua_State *L)
isbear@0: {
isbear@0: luaL_argcheck (L, lua_islightuserdata (L, 1), 1, "lm ssl lightuserdata expected");
isbear@11: bless_lm_ssl (L, lua_touserdata (L, 1));
isbear@0: return 1;
isbear@0: }
isbear@0:
isbear@0: /// lm.ssl.supported
isbear@57: /// Indicates if SSL is supported by loudmouth library and what kind of
isbear@57: /// ssl fingerprint is used.
isbear@57: /// R: nil or string ("MD5" or "SHA256")
isbear@11: static int supported_lm_ssl (lua_State *L)
isbear@0: {
isbear@57: if (lm_ssl_is_supported ()) {
isbear@57: #ifdef HAVE_LM_SHA256_FINGERPRINTS
isbear@57: lua_pushliteral (L, "SHA256");
isbear@57: #else
isbear@57: lua_pushliteral (L, "MD5");
isbear@57: #endif
isbear@57: } else {
isbear@57: lua_pushnil (L);
isbear@57: }
isbear@0: return 1;
isbear@0: }
isbear@0:
isbear@59: #ifdef HAVE_LM_SSL_SET_CA
isbear@59: /// ssl:ca_path
isbear@59: /// Set path to trusted ssl certificates. Argument must be a name of a PEM file
isbear@59: /// or a name of directory with hashed certificates.
isbear@59: /// A: string (path)
isbear@59: static int ca_path_lm_ssl (lua_State *L)
isbear@59: {
isbear@59: llm_ssl_t *object = luaL_checklm_ssl (L, 1);
isbear@59: const gchar *path = luaL_checkstring (L, 2);
isbear@59: lm_ssl_set_ca (object -> ssl, path);
isbear@59: return 0;
isbear@59: }
isbear@59: #endif
isbear@59:
isbear@59: #ifdef HAVE_LM_SSL_SET_CIPHER_LIST
isbear@59: /// ssl:cipher_list
isbear@59: /// Set list of allowed ciphers (colon-separated). Names may vary depending on ssl
isbear@59: /// implementation in use.
isbear@59: /// A: string (cipher list)
isbear@59: static int cipher_list_lm_ssl (lua_State *L)
isbear@59: {
isbear@59: llm_ssl_t *object = luaL_checklm_ssl (L, 1);
isbear@59: const gchar *list = luaL_checkstring (L, 2);
isbear@59: lm_ssl_set_cipher_list (object -> ssl, list);
isbear@59: return 0;
isbear@59: }
isbear@59: #endif
isbear@59:
isbear@0: /// ssl:fingerprint
isbear@0: /// Returns fingerprint of remote server.
isbear@0: /// R: string or nil
isbear@11: static int fingerprint_lm_ssl (lua_State *L)
isbear@0: {
isbear@0: llm_ssl_t *object = luaL_checklm_ssl (L, 1);
isbear@0: const gchar *fingerprint = lm_ssl_get_fingerprint (object->ssl);
isbear@0: if (fingerprint == NULL)
isbear@0: lua_pushnil (L);
isbear@0: else {
isbear@57: #ifdef HAVE_LM_SHA256_FINGERPRINTS
isbear@57: lua_pushstring (L, fingerprint);
isbear@57: #else
isbear@57: char buffer[48];
isbear@0: snprintf (buffer, 48,
isbear@0: "%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:"
isbear@0: "%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX",
isbear@0: fingerprint[0], fingerprint[1], fingerprint[2], fingerprint[3],
isbear@0: fingerprint[4], fingerprint[5], fingerprint[6], fingerprint[7],
isbear@0: fingerprint[8], fingerprint[9], fingerprint[10], fingerprint[11],
isbear@0: fingerprint[12], fingerprint[13], fingerprint[14], fingerprint[15]);
isbear@0: lua_pushlstring (L, buffer, 47);
isbear@57: #endif
isbear@0: }
isbear@0: return 1;
isbear@0: }
isbear@0:
isbear@42: /// ssl:tls
isbear@42: /// Sets or returns use of starttls by this ssl object.
isbear@42: /// A: boolean (use starttls), boolean (require starttls)
isbear@42: /// or
isbear@42: /// R: boolean (use starttls), boolean (require starttls)
isbear@42: static int tls_lm_ssl (lua_State *L)
isbear@42: {
isbear@42: llm_ssl_t *object = luaL_checklm_ssl (L, 1);
isbear@42: if (lua_gettop (L) > 1) {
isbear@42: gboolean use = lua_toboolean (L, 2);
isbear@42: gboolean require = lua_toboolean (L, 3);
isbear@42: lm_ssl_use_starttls (object -> ssl, use, require);
isbear@42: return 0;
isbear@42: } else {
isbear@42: lua_pushboolean (L, lm_ssl_get_use_starttls (object -> ssl));
isbear@42: lua_pushboolean (L, lm_ssl_get_require_starttls (object -> ssl));
isbear@42: return 2;
isbear@42: }
isbear@42: }
isbear@42:
isbear@0: /// ssl:pointer
isbear@0: /// Returns pointer to underlying C structure.
isbear@0: /// R: lightuserdata
isbear@11: static int pointer_lm_ssl (lua_State *L)
isbear@0: {
isbear@0: llm_ssl_t *object = luaL_checklm_ssl (L, 1);
isbear@0: lua_pushlightuserdata (L, object->ssl);
isbear@0: return 1;
isbear@0: }
isbear@0:
isbear@11: static int gc_lm_ssl (lua_State *L)
isbear@0: {
isbear@0: llm_ssl_t *object = luaL_checklm_ssl (L, 1);
isbear@38: D ("SSL %p gc called", object);
isbear@0: lm_ssl_unref (object->ssl);
isbear@0: return 0;
isbear@0: }
isbear@0:
isbear@11: const static luaL_Reg reg_f_lm_ssl[] = {
isbear@11: { "new", new_lm_ssl },
isbear@11: { "bless", bless_lua_lm_ssl },
isbear@11: { "supported", supported_lm_ssl },
isbear@12: { NULL, NULL },
isbear@0: };
isbear@0:
isbear@11: const static luaL_Reg reg_m_lm_ssl[] = {
isbear@59: #ifdef HAVE_LM_SSL_SET_CA
isbear@59: { "ca_path", ca_path_lm_ssl },
isbear@59: #endif
isbear@59: #ifdef HAVE_LM_SSL_SET_CIPHER_LIST
isbear@59: { "cipher_list", cipher_list_lm_ssl },
isbear@59: #endif
isbear@11: { "fingerprint", fingerprint_lm_ssl },
isbear@42: { "tls", tls_lm_ssl },
isbear@11: { "pointer", pointer_lm_ssl },
isbear@11: { "__gc", gc_lm_ssl },
isbear@12: { NULL, NULL },
isbear@0: };
isbear@0:
isbear@0: int luaopen_lm_ssl (lua_State *L)
isbear@0: {
isbear@0: luaL_newmetatable (L, "loudmouth.ssl");
isbear@19: lua_pushvalue (L, -1);
isbear@19: lua_setfield (L, -2, "__index");
isbear@54: luaL_setfuncs (L, reg_m_lm_ssl, 0);
isbear@0: lua_pop (L, 1);
isbear@16: lua_newtable (L); // XXX we can specify here exact amount of fields
isbear@54: luaL_setfuncs (L, reg_f_lm_ssl, 0);
isbear@0: return 1;
isbear@0: }
isbear@0: