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@62: /// R: lm ssl object 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@62: lua_pop (L, 1); isbear@62: return 1; 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@62: /// R: lm ssl object 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@62: lua_pop (L, 1); isbear@62: return 1; 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: