diff --git a/Makefile b/Makefile index ae688c6..93fbe77 100644 --- a/Makefile +++ b/Makefile @@ -2,16 +2,20 @@ LUAPATH=/usr/local/share/lua/5.1 LUACPATH=/usr/local/lib/lua/5.1 -# Edit the lines below to inform new path, if necessary -# -#INCDIR=-I/usr/local/lua-5.1/include -I/usr/local/openssl-0.9.8/include -#LIBDIR=-L/usr/local/openssl-0.9.8/lib -R/usr/local/openssl-0.9.8/lib +# Compile with build-in LuaSocket's help files. +# Comment this lines if you will link with non-internal LuaSocket's help files +# and edit INCDIR and LIBDIR properly. +EXTRA=luasocket +DEFS=-DWITH_LUASOCKET + +# Edit the lines below to inform new path, if necessary. +# Path below points to internal LuaSocket's help files. +INCDIR=-I. +LIBDIR=-L./luasocket # For Mac OS X: set the system version MACOSX_VERSION=10.4 -DEFS=-DBUFFER_DEBUG - #---------------------- # Do not edit this part @@ -26,25 +30,25 @@ none: @echo " * macosx" install: - @cd src ; $(MAKE) LUACPATH="$(LUACPATH)" LUAPATH="$(LUAPATH)" install + @cd src && $(MAKE) LUACPATH="$(LUACPATH)" LUAPATH="$(LUAPATH)" install linux: @echo "---------------------" @echo "** Build for Linux **" @echo "---------------------" - @cd src ; $(MAKE) INCDIR="$(INCDIR)" LIBDIR="$(LIBDIR)" DEFS="$(DEFS)" $@ + @cd src && $(MAKE) INCDIR="$(INCDIR)" LIBDIR="$(LIBDIR)" DEFS="$(DEFS)" EXTRA="$(EXTRA)" $@ bsd: @echo "-------------------" @echo "** Build for BSD **" @echo "-------------------" - @cd src ; $(MAKE) INCDIR="$(INCDIR)" LIBDIR="$(LIBDIR)" DEFS="$(DEFS)" $@ + @cd src && $(MAKE) INCDIR="$(INCDIR)" LIBDIR="$(LIBDIR)" DEFS="$(DEFS)" EXTRA="$(EXTRA)" $@ macosx: @echo "------------------------------" @echo "** Build for Mac OS X $(MACOSX_VERSION) **" @echo "------------------------------" - @cd src ; $(MAKE) INCDIR="$(INCDIR)" LIBDIR="$(LIBDIR)" DEFS="$(DEFS)" MACVER="$(MACOSX_VERSION)" $@ + @cd src && $(MAKE) INCDIR="$(INCDIR)" LIBDIR="$(LIBDIR)" MACVER="$(MACOSX_VERSION)" DEFS="$(DEFS)" EXTRA="$(EXTRA)" $@ clean: - @cd src ; $(MAKE) clean + @cd src && $(MAKE) clean diff --git a/luasec.sln b/luasec.sln index b34bdfd..94d23d9 100644 --- a/luasec.sln +++ b/luasec.sln @@ -1,6 +1,6 @@ -Microsoft Visual Studio Solution File, Format Version 10.00 -# Visual C++ Express 2008 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "luasec", "luasec.vcproj", "{A629932F-8819-4C0B-8835-CBF1FEED6376}" +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual C++ Express 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "luasec", "luasec.vcxproj", "{A629932F-8819-4C0B-8835-CBF1FEED6376}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/luasec.suo b/luasec.suo index 3de85c6..33c275e 100644 Binary files a/luasec.suo and b/luasec.suo differ diff --git a/luasec.vcxproj b/luasec.vcxproj new file mode 100644 index 0000000..3d2905a --- /dev/null +++ b/luasec.vcxproj @@ -0,0 +1,125 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {A629932F-8819-4C0B-8835-CBF1FEED6376} + Win32Proj + + + + DynamicLibrary + MultiByte + + + DynamicLibrary + MultiByte + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + Debug\ + Debug\ + true + Release\ + Release\ + false + ssl + + + + Disabled + C:\devel\openssl\include;C:\devel\lua-dll9\include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_USRDLL;LUASEC_EXPORTS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + EditAndContinue + + + ws2_32.lib;libeay32MDd.lib;ssleay32MDd.lib;lua5.1.lib;%(AdditionalDependencies) + $(OutDir)ssl.dll + C:\devel\openssl\lib\VC;C:\devel\lua-dll9;%(AdditionalLibraryDirectories) + true + $(OutDir)luasec.pdb + Windows + false + + + $(OutDir)ssl.lib + MachineX86 + + + + + C:\devel\openssl\include;C:\devel\lua5.2\include;.\src;%(AdditionalIncludeDirectories) + WIN32;_WIN32;NDEBUG;_WINDOWS;_USRDLL;LUASOCKET_DEBUG;WITH_LUASOCKET;%(PreprocessorDefinitions) + MultiThreaded + + + Level3 + ProgramDatabase + + + ws2_32.lib;libeay32MD.lib;ssleay32MD.lib;lua52.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + C:\devel\openssl\lib\VC;C:\devel\lua5.2\lib;%(AdditionalLibraryDirectories) + true + Windows + true + true + false + + + $(OutDir)ssl.lib + MachineX86 + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/luasec.vcxproj.filters b/luasec.vcxproj.filters new file mode 100644 index 0000000..06da575 --- /dev/null +++ b/luasec.vcxproj.filters @@ -0,0 +1,75 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/samples/certs/serverA.cnf b/samples/certs/serverA.cnf index f86b6b6..b8d18fc 100644 --- a/samples/certs/serverA.cnf +++ b/samples/certs/serverA.cnf @@ -172,7 +172,7 @@ basicConstraints=CA:FALSE # the certificate can be used for anything *except* object signing. # This is OK for an SSL server. -# nsCertType = server +nsCertType = server # For an object signing certificate this would be used. # nsCertType = objsign diff --git a/samples/certs/serverB.cnf b/samples/certs/serverB.cnf index 2a1426a..344c140 100644 --- a/samples/certs/serverB.cnf +++ b/samples/certs/serverB.cnf @@ -172,7 +172,7 @@ basicConstraints=CA:FALSE # the certificate can be used for anything *except* object signing. # This is OK for an SSL server. -# nsCertType = server +nsCertType = server # For an object signing certificate this would be used. # nsCertType = objsign diff --git a/samples/chain/client.lua b/samples/chain/client.lua new file mode 100644 index 0000000..410c6d8 --- /dev/null +++ b/samples/chain/client.lua @@ -0,0 +1,36 @@ +-- +-- Public domain +-- +local socket = require("socket") +local ssl = require("ssl") +local util = require("util") + +local params = { + mode = "client", + protocol = "tlsv1", + key = "../certs/clientAkey.pem", + certificate = "../certs/clientA.pem", + cafile = "../certs/rootA.pem", + verify = {"peer", "fail_if_no_peer_cert"}, + options = {"all", "no_sslv2"}, +} + +local conn = socket.tcp() +conn:connect("127.0.0.1", 8888) + +conn = assert( ssl.wrap(conn, params) ) +assert(conn:dohandshake()) + +util.show( conn:getpeercertificate() ) + +print("----------------------------------------------------------------------") + +for k, cert in ipairs( conn:getpeerchain() ) do + util.show(cert) +end + +local cert = conn:getpeercertificate() +print( cert ) +print( cert:pem() ) + +conn:close() diff --git a/samples/chain/server.lua b/samples/chain/server.lua new file mode 100644 index 0000000..9a6c4dc --- /dev/null +++ b/samples/chain/server.lua @@ -0,0 +1,53 @@ +-- +-- Public domain +-- +local socket = require("socket") +local ssl = require("ssl") +local util = require("util") + +local params = { + mode = "server", + protocol = "tlsv1", + key = "../certs/serverAkey.pem", + certificate = "../certs/serverA.pem", + cafile = "../certs/rootA.pem", + verify = {"peer", "fail_if_no_peer_cert"}, + options = {"all", "no_sslv2"}, +} + +local ctx = assert(ssl.newcontext(params)) + +local server = socket.tcp() +server:setoption('reuseaddr', true) +assert( server:bind("127.0.0.1", 8888) ) +server:listen() + +local conn = server:accept() + +conn = assert( ssl.wrap(conn, ctx) ) +assert( conn:dohandshake() ) + +util.show( conn:getpeercertificate() ) + +print("----------------------------------------------------------------------") + +for k, cert in ipairs( conn:getpeerchain() ) do + util.show(cert) +end + +local f = io.open(params.certificate) +local str = f:read("*a") +f:close() + +util.show( ssl.loadcertificate(str) ) + +print("----------------------------------------------------------------------") +local cert = conn:getpeercertificate() +print( cert ) +print( cert:digest() ) +print( cert:digest("sha1") ) +print( cert:digest("sha256") ) +print( cert:digest("sha512") ) + +conn:close() +server:close() diff --git a/samples/chain/util.lua b/samples/chain/util.lua new file mode 100644 index 0000000..1b556b0 --- /dev/null +++ b/samples/chain/util.lua @@ -0,0 +1,22 @@ +local print = print +local ipairs = ipairs + +local _ENV = {} + +function _ENV.show(cert) + print("Serial:", cert:serial()) + print("NotBefore:", cert:notbefore()) + print("NotAfter:", cert:notafter()) + print("--- Issuer ---") + for k, v in ipairs(cert:issuer()) do + print(v.name .. " = " .. v.value) + end + + print("--- Subject ---") + for k, v in ipairs(cert:subject()) do + print(v.name .. " = " .. v.value) + end + print("----------------------------------------------------------------------") +end + +return _ENV diff --git a/samples/dhparam/client.lua b/samples/dhparam/client.lua new file mode 100644 index 0000000..202fb12 --- /dev/null +++ b/samples/dhparam/client.lua @@ -0,0 +1,26 @@ +-- +-- Public domain +-- +local socket = require("socket") +local ssl = require("ssl") + +local params = { + mode = "client", + protocol = "sslv3", + key = "../certs/clientAkey.pem", + certificate = "../certs/clientA.pem", + cafile = "../certs/rootA.pem", + verify = {"peer", "fail_if_no_peer_cert"}, + options = {"all", "no_sslv2"}, +} + +local peer = socket.tcp() +peer:connect("127.0.0.1", 8888) + +-- [[ SSL wrapper +peer = assert( ssl.wrap(peer, params) ) +assert(peer:dohandshake()) +--]] + +print(peer:receive("*l")) +peer:close() diff --git a/samples/dhparam/params.sh b/samples/dhparam/params.sh new file mode 100644 index 0000000..8e4f031 --- /dev/null +++ b/samples/dhparam/params.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +openssl dhparam -2 -out dh-512.pem -outform PEM 512 +openssl dhparam -2 -out dh-1024.pem -outform PEM 1024 diff --git a/samples/dhparam/server.lua b/samples/dhparam/server.lua new file mode 100644 index 0000000..e8594e5 --- /dev/null +++ b/samples/dhparam/server.lua @@ -0,0 +1,61 @@ +-- +-- Public domain +-- +local socket = require("socket") +local ssl = require("ssl") + +local function readfile(filename) + local fd = assert(io.open(filename)) + local dh = fd:read("*a") + fd:close() + return dh +end + +local function dhparam_cb(export, keylength) + print("---") + print("DH Callback") + print("Export", export) + print("Key length", keylength) + print("---") + local filename + if keylength == 512 then + filename = "dh-512.pem" + elseif keylength == 1024 then + filename = "dh-1024.pem" + else + -- No key + return nil + end + return readfile(filename) +end + +local params = { + mode = "server", + protocol = "sslv3", + key = "../certs/serverAkey.pem", + certificate = "../certs/serverA.pem", + cafile = "../certs/rootA.pem", + verify = {"peer", "fail_if_no_peer_cert"}, + options = {"all", "no_sslv2"}, + dhparam = dhparam_cb, +} + + +-- [[ SSL context +local ctx = assert(ssl.newcontext(params)) +--]] + +local server = socket.tcp() +server:setoption('reuseaddr', true) +assert( server:bind("127.0.0.1", 8888) ) +server:listen() + +local peer = server:accept() + +-- [[ SSL wrapper +peer = assert( ssl.wrap(peer, ctx) ) +assert( peer:dohandshake() ) +--]] + +peer:send("oneshot test\n") +peer:close() diff --git a/samples/digest/client.lua b/samples/digest/client.lua new file mode 100644 index 0000000..202fb12 --- /dev/null +++ b/samples/digest/client.lua @@ -0,0 +1,26 @@ +-- +-- Public domain +-- +local socket = require("socket") +local ssl = require("ssl") + +local params = { + mode = "client", + protocol = "sslv3", + key = "../certs/clientAkey.pem", + certificate = "../certs/clientA.pem", + cafile = "../certs/rootA.pem", + verify = {"peer", "fail_if_no_peer_cert"}, + options = {"all", "no_sslv2"}, +} + +local peer = socket.tcp() +peer:connect("127.0.0.1", 8888) + +-- [[ SSL wrapper +peer = assert( ssl.wrap(peer, params) ) +assert(peer:dohandshake()) +--]] + +print(peer:receive("*l")) +peer:close() diff --git a/samples/digest/server.lua b/samples/digest/server.lua new file mode 100644 index 0000000..9af34fe --- /dev/null +++ b/samples/digest/server.lua @@ -0,0 +1,44 @@ +-- +-- Public domain +-- +local socket = require("socket") +local ssl = require("ssl") + +local params = { + mode = "server", + protocol = "sslv3", + key = "../certs/serverAkey.pem", + certificate = "../certs/serverA.pem", + cafile = "../certs/rootA.pem", + verify = {"peer", "fail_if_no_peer_cert"}, + options = {"all", "no_sslv2"}, +} + + +-- [[ SSL context +local ctx = assert(ssl.newcontext(params)) +--]] + +local server = socket.tcp() +server:setoption('reuseaddr', true) +assert( server:bind("127.0.0.1", 8888) ) +server:listen() + +local peer = server:accept() + +-- [[ SSL wrapper +peer = assert( ssl.wrap(peer, ctx) ) +assert( peer:dohandshake() ) +--]] + +local cert = peer:getpeercertificate() +local sha1 = cert:digest("sha1") +local sha256 = cert:digest("sha256") +local sha512 = cert:digest("sha512") + +print("SHA1", sha1) +print("SHA256", sha256) +print("SHA512", sha512) + +peer:send("oneshot test\n") +peer:close() diff --git a/samples/ecdh/client.lua b/samples/ecdh/client.lua new file mode 100644 index 0000000..5f0344d --- /dev/null +++ b/samples/ecdh/client.lua @@ -0,0 +1,33 @@ +-- +-- Public domain +-- +local socket = require("socket") +local ssl = require("ssl") + +local params = { + mode = "client", + protocol = "sslv3", + key = "../certs/clientAkey.pem", + certificate = "../certs/clientA.pem", + cafile = "../certs/rootA.pem", + verify = {"peer", "fail_if_no_peer_cert"}, + options = {"all", "no_sslv2"}, + -- + curve = "secp384r1", +} + +-------------------------------------------------------------------------------- +local peer = socket.tcp() +peer:connect("127.0.0.1", 8888) + +peer = assert( ssl.wrap(peer, params) ) +assert(peer:dohandshake()) + +print("--- INFO ---") +local info = peer:info() +for k, v in pairs(info) do + print(k, v) +end +print("---") + +peer:close() diff --git a/samples/ecdh/server.lua b/samples/ecdh/server.lua new file mode 100644 index 0000000..f8c3dd5 --- /dev/null +++ b/samples/ecdh/server.lua @@ -0,0 +1,40 @@ +-- +-- Public domain +-- +local socket = require("socket") +local ssl = require("ssl") + +local params = { + mode = "server", + protocol = "sslv3", + key = "../certs/serverAkey.pem", + certificate = "../certs/serverA.pem", + cafile = "../certs/rootA.pem", + verify = {"peer", "fail_if_no_peer_cert"}, + options = {"all", "no_sslv2"}, + -- + curve = "secp384r1", +} + +------------------------------------------------------------------------------ +local ctx = assert(ssl.newcontext(params)) + +local server = socket.tcp() +server:setoption('reuseaddr', true) +assert( server:bind("127.0.0.1", 8888) ) +server:listen() + +local peer = server:accept() + +peer = assert( ssl.wrap(peer, ctx) ) +assert( peer:dohandshake() ) + +print("--- INFO ---") +local info = peer:info() +for k, v in pairs(info) do + print(k, v) +end +print("---") + +peer:close() +server:close() diff --git a/samples/info/client.lua b/samples/info/client.lua new file mode 100644 index 0000000..202fb12 --- /dev/null +++ b/samples/info/client.lua @@ -0,0 +1,26 @@ +-- +-- Public domain +-- +local socket = require("socket") +local ssl = require("ssl") + +local params = { + mode = "client", + protocol = "sslv3", + key = "../certs/clientAkey.pem", + certificate = "../certs/clientA.pem", + cafile = "../certs/rootA.pem", + verify = {"peer", "fail_if_no_peer_cert"}, + options = {"all", "no_sslv2"}, +} + +local peer = socket.tcp() +peer:connect("127.0.0.1", 8888) + +-- [[ SSL wrapper +peer = assert( ssl.wrap(peer, params) ) +assert(peer:dohandshake()) +--]] + +print(peer:receive("*l")) +peer:close() diff --git a/samples/info/server.lua b/samples/info/server.lua new file mode 100644 index 0000000..a22686a --- /dev/null +++ b/samples/info/server.lua @@ -0,0 +1,48 @@ +-- +-- Public domain +-- +local socket = require("socket") +local ssl = require("ssl") + +local params = { + mode = "server", + protocol = "sslv3", + key = "../certs/serverAkey.pem", + certificate = "../certs/serverA.pem", + cafile = "../certs/rootA.pem", + verify = {"peer", "fail_if_no_peer_cert"}, + options = {"all", "no_sslv2"}, +} + + +-- [[ SSL context +local ctx = assert(ssl.newcontext(params)) +--]] + +local server = socket.tcp() +server:setoption('reuseaddr', true) +assert( server:bind("127.0.0.1", 8888) ) +server:listen() + +local peer = server:accept() + +-- [[ SSL wrapper +peer = assert( ssl.wrap(peer, ctx) ) + +-- Before handshake: nil +print( peer:info() ) + +assert( peer:dohandshake() ) +--]] + +print("---") +local info = peer:info() +for k, v in pairs(info) do + print(k, v) +end + +print("---") +print("-> Compression", peer:info("compression")) + +peer:send("oneshot test\n") +peer:close() diff --git a/samples/key/loadkey.lua b/samples/key/loadkey.lua index 274d0e4..f9c3840 100644 --- a/samples/key/loadkey.lua +++ b/samples/key/loadkey.lua @@ -1,7 +1,7 @@ -- -- Public domain -- -require("ssl") +local ssl = require("ssl") local pass = "foobar" local cfg = { diff --git a/samples/loop-gc/client.lua b/samples/loop-gc/client.lua index bcf5f8d..0a47d0a 100644 --- a/samples/loop-gc/client.lua +++ b/samples/loop-gc/client.lua @@ -1,8 +1,8 @@ -- -- Public domain -- -require("socket") -require("ssl") +local socket = require("socket") +local ssl = require("ssl") local params = { mode = "client", diff --git a/samples/loop-gc/server.lua b/samples/loop-gc/server.lua index 32e48f9..89470cc 100644 --- a/samples/loop-gc/server.lua +++ b/samples/loop-gc/server.lua @@ -1,8 +1,8 @@ -- -- Public domain -- -require("socket") -require("ssl") +local socket = require("socket") +local ssl = require("ssl") local params = { mode = "server", diff --git a/samples/loop/client.lua b/samples/loop/client.lua index 13cd720..a0a1e2d 100644 --- a/samples/loop/client.lua +++ b/samples/loop/client.lua @@ -1,8 +1,8 @@ -- -- Public domain -- -require("socket") -require("ssl") +local socket = require("socket") +local ssl = require("ssl") local params = { mode = "client", diff --git a/samples/loop/server.lua b/samples/loop/server.lua index f35b8a9..496d05c 100644 --- a/samples/loop/server.lua +++ b/samples/loop/server.lua @@ -1,8 +1,8 @@ -- -- Public domain -- -require("socket") -require("ssl") +local socket = require("socket") +local ssl = require("ssl") local params = { mode = "server", diff --git a/samples/oneshot/client.lua b/samples/oneshot/client.lua index 5ecf492..202fb12 100644 --- a/samples/oneshot/client.lua +++ b/samples/oneshot/client.lua @@ -1,8 +1,8 @@ -- -- Public domain -- -require("socket") -require("ssl") +local socket = require("socket") +local ssl = require("ssl") local params = { mode = "client", diff --git a/samples/oneshot/server.lua b/samples/oneshot/server.lua index 93261e2..233a825 100644 --- a/samples/oneshot/server.lua +++ b/samples/oneshot/server.lua @@ -1,8 +1,8 @@ -- -- Public domain -- -require("socket") -require("ssl") +local socket = require("socket") +local ssl = require("ssl") local params = { mode = "server", diff --git a/samples/verification/fail-string/client.lua b/samples/verification/fail-string/client.lua new file mode 100644 index 0000000..7a45212 --- /dev/null +++ b/samples/verification/fail-string/client.lua @@ -0,0 +1,29 @@ +-- +-- Public domain +-- +local socket = require("socket") +local ssl = require("ssl") + +local params = { + mode = "client", + protocol = "sslv3", + key = "../../certs/clientBkey.pem", + certificate = "../../certs/clientB.pem", + cafile = "../../certs/rootB.pem", + verify = {"none"}, + options = {"all", "no_sslv2"}, +} + +local peer = socket.tcp() +peer:connect("127.0.0.1", 8888) + +-- [[ SSL wrapper +peer = assert( ssl.wrap(peer, params) ) +assert(peer:dohandshake()) +--]] + +local err, msg = peer:getpeerverification() +print(err, msg) + +print(peer:receive("*l")) +peer:close() diff --git a/samples/verification/fail-string/server.lua b/samples/verification/fail-string/server.lua new file mode 100644 index 0000000..3bbf013 --- /dev/null +++ b/samples/verification/fail-string/server.lua @@ -0,0 +1,38 @@ +-- +-- Public domain +-- +local socket = require("socket") +local ssl = require("ssl") + +local params = { + mode = "server", + protocol = "sslv3", + key = "../../certs/serverAkey.pem", + certificate = "../../certs/serverA.pem", + cafile = "../../certs/rootA.pem", + verify = {"none"}, + options = {"all", "no_sslv2"}, +} + + +-- [[ SSL context +local ctx = assert(ssl.newcontext(params)) +--]] + +local server = socket.tcp() +server:setoption('reuseaddr', true) +assert( server:bind("127.0.0.1", 8888) ) +server:listen() + +local peer = server:accept() + +-- [[ SSL wrapper +peer = assert( ssl.wrap(peer, ctx) ) +assert( peer:dohandshake() ) +--]] + +local err, msg = peer:getpeerverification() +print(err, msg) + +peer:send("oneshot test\n") +peer:close() diff --git a/samples/verification/fail-table/client.lua b/samples/verification/fail-table/client.lua new file mode 100644 index 0000000..a8f3874 --- /dev/null +++ b/samples/verification/fail-table/client.lua @@ -0,0 +1,40 @@ +-- +-- Public domain +-- +local socket = require("socket") +local ssl = require("ssl") + +local params = { + mode = "client", + protocol = "sslv3", + key = "../../certs/clientBkey.pem", + certificate = "../../certs/clientB.pem", + cafile = "../../certs/rootB.pem", + verify = {"peer", "fail_if_no_peer_cert"}, + options = {"all", "no_sslv2"}, +} + +-- [[ SSL context +local ctx = assert(ssl.newcontext(params)) +--]] + +ctx:setverifyext("lsec_continue") + +local peer = socket.tcp() +peer:connect("127.0.0.1", 8888) + +-- [[ SSL wrapper +peer = assert( ssl.wrap(peer, ctx) ) +assert(peer:dohandshake()) +--]] + +local succ, errs = peer:getpeerverification() +print(succ, errs) +for i, err in pairs(errs) do + for j, msg in ipairs(err) do + print("depth = " .. i, "error = " .. msg) + end +end + +print(peer:receive("*l")) +peer:close() diff --git a/samples/verification/fail-table/server.lua b/samples/verification/fail-table/server.lua new file mode 100644 index 0000000..47112af --- /dev/null +++ b/samples/verification/fail-table/server.lua @@ -0,0 +1,45 @@ +-- +-- Public domain +-- +local socket = require("socket") +local ssl = require("ssl") + +local params = { + mode = "server", + protocol = "sslv3", + key = "../../certs/serverAkey.pem", + certificate = "../../certs/serverA.pem", + cafile = "../../certs/rootA.pem", + verify = {"peer", "fail_if_no_peer_cert"}, + options = {"all", "no_sslv2"}, +} + + +-- [[ SSL context +local ctx = assert(ssl.newcontext(params)) +--]] + +ctx:setverifyext("lsec_continue", "crl_check", "crl_check_chain") + +local server = socket.tcp() +server:setoption('reuseaddr', true) +assert( server:bind("127.0.0.1", 8888) ) +server:listen() + +local peer = server:accept() + +-- [[ SSL wrapper +peer = assert( ssl.wrap(peer, ctx) ) +assert( peer:dohandshake() ) +--]] + +local succ, errs = peer:getpeerverification() +print(succ, errs) +for i, err in pairs(errs) do + for j, msg in ipairs(err) do + print("depth = " .. i, "error = " .. msg) + end +end + +peer:send("oneshot test\n") +peer:close() diff --git a/samples/verification/success/client.lua b/samples/verification/success/client.lua new file mode 100644 index 0000000..996940b --- /dev/null +++ b/samples/verification/success/client.lua @@ -0,0 +1,29 @@ +-- +-- Public domain +-- +local socket = require("socket") +local ssl = require("ssl") + +local params = { + mode = "client", + protocol = "sslv3", + key = "../../certs/clientAkey.pem", + certificate = "../../certs/clientA.pem", + cafile = "../../certs/rootA.pem", + verify = {"peer", "fail_if_no_peer_cert"}, + options = {"all", "no_sslv2"}, +} + +local peer = socket.tcp() +peer:connect("127.0.0.1", 8888) + +-- [[ SSL wrapper +peer = assert( ssl.wrap(peer, params) ) +assert(peer:dohandshake()) +--]] + +local err, msg = peer:getpeerverification() +print(err, msg) + +print(peer:receive("*l")) +peer:close() diff --git a/samples/verification/success/server.lua b/samples/verification/success/server.lua new file mode 100644 index 0000000..fb75b6d --- /dev/null +++ b/samples/verification/success/server.lua @@ -0,0 +1,38 @@ +-- +-- Public domain +-- +local socket = require("socket") +local ssl = require("ssl") + +local params = { + mode = "server", + protocol = "sslv3", + key = "../../certs/serverAkey.pem", + certificate = "../../certs/serverA.pem", + cafile = "../../certs/rootA.pem", + verify = {"peer", "fail_if_no_peer_cert"}, + options = {"all", "no_sslv2"}, +} + + +-- [[ SSL context +local ctx = assert(ssl.newcontext(params)) +--]] + +local server = socket.tcp() +server:setoption('reuseaddr', true) +assert( server:bind("127.0.0.1", 8888) ) +server:listen() + +local peer = server:accept() + +-- [[ SSL wrapper +peer = assert( ssl.wrap(peer, ctx) ) +assert( peer:dohandshake() ) +--]] + +local err, msg = peer:getpeerverification() +print(err, msg) + +peer:send("oneshot test\n") +peer:close() diff --git a/samples/verify/client.lua b/samples/verify/client.lua new file mode 100644 index 0000000..709237a --- /dev/null +++ b/samples/verify/client.lua @@ -0,0 +1,40 @@ +-- +-- Public domain +-- +local socket = require("socket") +local ssl = require("ssl") + +local params = { + mode = "client", + protocol = "tlsv1", + key = "../certs/serverBkey.pem", + certificate = "../certs/serverB.pem", + cafile = "../certs/rootB.pem", + verify = {"peer", "fail_if_no_peer_cert"}, + options = {"all", "no_sslv2"}, +} + +local ctx = assert(ssl.newcontext(params)) + +-- [[ Ignore error on certificate verification +ctx:setverifyext("lsec_continue") +--ctx:setverifyext("lsec_ignore_purpose") +--ctx:setverifyext(); -- Clear all flags set +--]] + +local peer = socket.tcp() +peer:connect("127.0.0.1", 8888) + +peer = assert( ssl.wrap(peer, ctx) ) +assert(peer:dohandshake()) + +local succ, errs = peer:getpeerverification() +print(succ, errs) +for i, err in pairs(errs) do + for j, msg in ipairs(err) do + print("depth = " .. i, "error = " .. msg) + end +end + +print(peer:receive("*l")) +peer:close() diff --git a/samples/verify/server.lua b/samples/verify/server.lua new file mode 100644 index 0000000..8b9bae0 --- /dev/null +++ b/samples/verify/server.lua @@ -0,0 +1,45 @@ +-- +-- Public domain +-- +local socket = require("socket") +local ssl = require("ssl") + +local params = { + mode = "server", + protocol = "tlsv1", + key = "../certs/serverAkey.pem", + certificate = "../certs/serverA.pem", + cafile = "../certs/rootA.pem", + verify = {"peer", "fail_if_no_peer_cert"}, + options = {"all", "no_sslv2"}, +} + + +local ctx = assert(ssl.newcontext(params)) + +-- [[ Ignore error on certificate verification +ctx:setverifyext("lsec_continue") +--ctx:setverifyext("lsec_ignore_purpose") +--ctx:setverifyext(); -- Clear all flags set +--]] + +local server = socket.tcp() +server:setoption('reuseaddr', true) +assert( server:bind("127.0.0.1", 8888) ) +server:listen() + +local peer = server:accept() + +peer = assert( ssl.wrap(peer, ctx) ) +assert( peer:dohandshake() ) + +local succ, errs = peer:getpeerverification() +print(succ, errs) +for i, err in pairs(errs) do + for j, msg in ipairs(err) do + print("depth = " .. i, "error = " .. msg) + end +end + +peer:send("oneshot test\n") +peer:close() diff --git a/samples/want/client.lua b/samples/want/client.lua index 586f0a4..3987aa8 100644 --- a/samples/want/client.lua +++ b/samples/want/client.lua @@ -3,8 +3,8 @@ -- -- Public domain -- -require("socket") -require("ssl") +local socket = require("socket") +local ssl = require("ssl") local params = { mode = "client", diff --git a/samples/want/server.lua b/samples/want/server.lua index 4a874b6..14f7c12 100644 --- a/samples/want/server.lua +++ b/samples/want/server.lua @@ -1,8 +1,8 @@ -- -- Public domain -- -require("socket") -require("ssl") +local socket = require("socket") +local ssl = require("ssl") local params = { mode = "server", diff --git a/samples/wantread/client.lua b/samples/wantread/client.lua index 36112df..38800df 100644 --- a/samples/wantread/client.lua +++ b/samples/wantread/client.lua @@ -1,8 +1,8 @@ -- -- Public domain -- -require("socket") -require("ssl") +local socket = require("socket") +local ssl = require("ssl") local params = { mode = "client", diff --git a/samples/wantread/server.lua b/samples/wantread/server.lua index 0be23a5..44cf8b3 100644 --- a/samples/wantread/server.lua +++ b/samples/wantread/server.lua @@ -3,8 +3,8 @@ -- -- Public domain -- -require("socket") -require("ssl") +local socket = require("socket") +local ssl = require("ssl") local params = { mode = "server", diff --git a/samples/wantwrite/client.lua b/samples/wantwrite/client.lua index c0e7be1..09b7003 100644 --- a/samples/wantwrite/client.lua +++ b/samples/wantwrite/client.lua @@ -1,8 +1,8 @@ -- -- Public domain -- -require("socket") -require("ssl") +local socket = require("socket") +local ssl = require("ssl") local params = { mode = "client", diff --git a/samples/wantwrite/server.lua b/samples/wantwrite/server.lua index 7d73dce..fadad6b 100644 --- a/samples/wantwrite/server.lua +++ b/samples/wantwrite/server.lua @@ -1,8 +1,8 @@ -- -- Public domain -- -require("socket") -require("ssl") +local socket = require("socket") +local ssl = require("ssl") print("Use Ctrl+S and Ctrl+Q to suspend and resume the server.") diff --git a/src/Makefile b/src/Makefile index 9b9520d..0358f00 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2,14 +2,11 @@ CMOD=ssl.so LMOD=ssl.lua OBJS= \ - timeout.o \ - buffer.o \ - io.o \ - usocket.o \ + x509.o \ context.o \ ssl.o -LIBS=-lssl -lcrypto +LIBS=-lssl -lcrypto -lluasocket WARN=-Wall -pedantic @@ -28,35 +25,34 @@ LD=$(MYENV) gcc CFLAGS=$(MYCFLAGS) LDFLAGS=$(MYLDFLAGS) -.PHONY: all clean install none linux bsd macosx +.PHONY: all clean install none linux bsd macosx luasocket all: install: $(CMOD) $(LMOD) - mkdir -p $(LUAPATH)/ssl - cp $(CMOD) $(LUACPATH) - cp $(LMOD) $(LUAPATH) - cp https.lua $(LUAPATH)/ssl + install -m644 -D $(CMOD) $(LUACPATH) + install -m644 -D $(LMOD) $(LUAPATH) + install -m644 -D https.lua $(LUAPATH)/ssl linux: - @$(MAKE) $(CMOD) MYCFLAGS="$(LNX_CFLAGS)" MYLDFLAGS="$(LNX_LDFLAGS)" + @$(MAKE) $(CMOD) MYCFLAGS="$(LNX_CFLAGS)" MYLDFLAGS="$(LNX_LDFLAGS)" EXTRA="$(EXTRA)" bsd: - @$(MAKE) $(CMOD) MYCFLAGS="$(BSD_CFLAGS)" MYLDFLAGS="$(BSD_LDFLAGS)" + @$(MAKE) $(CMOD) MYCFLAGS="$(BSD_CFLAGS)" MYLDFLAGS="$(BSD_LDFLAGS)" EXTRA="$(EXTRA)" macosx: - @$(MAKE) $(CMOD) MYCFLAGS="$(MAC_CFLAGS)" MYLDFLAGS="$(MAC_LDFLAGS)" MYENV="$(MAC_ENV)" + @$(MAKE) $(CMOD) MYCFLAGS="$(MAC_CFLAGS)" MYLDFLAGS="$(MAC_LDFLAGS)" MYENV="$(MAC_ENV)" EXTRA="$(EXTRA)" +luasocket: + @cd luasocket && $(MAKE) -$(CMOD): $(OBJS) +$(CMOD): $(EXTRA) $(OBJS) $(LD) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) clean: + cd luasocket && $(MAKE) clean rm -f $(OBJS) $(CMOD) -buffer.o: buffer.c buffer.h io.h timeout.h -io.o: io.c io.h timeout.h -timeout.o: timeout.c timeout.h -usocket.o: usocket.c socket.h io.h timeout.h usocket.h -context.o: context.c context.h -ssl.o: ssl.c socket.h io.h timeout.h usocket.h buffer.h context.h context.c +x509.o: x509.c x509.h config.h +context.o: context.c context.h ec.h config.h +ssl.o: ssl.c ssl.h context.h x509.h config.h diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..0e1b5e7 --- /dev/null +++ b/src/config.h @@ -0,0 +1,14 @@ +#ifndef LSEC_CONFIG_H +#define LSEC_CONFIG_H + +#if defined(_WIN32) +#define LSEC_API __declspec(dllexport) +#else +#define LSEC_API extern +#endif + +#if (LUA_VERSION_NUM == 501) +#define lua_rawlen(L, i) lua_objlen(L, i) +#endif + +#endif diff --git a/src/context.c b/src/context.c index 53e3d3a..625468e 100644 --- a/src/context.c +++ b/src/context.c @@ -5,8 +5,15 @@ *--------------------------------------------------------------------------*/ #include + +#if defined(WIN32) +#include +#endif + #include #include +#include +#include #include #include @@ -14,6 +21,17 @@ #include "context.h" #include "options.h" +#ifndef OPENSSL_NO_ECDH +#include +#include "ec.h" +#endif + +#if (OPENSSL_VERSION_NUMBER >= 0x1000000fL) +typedef const SSL_METHOD LSEC_SSL_METHOD; +#else +typedef SSL_METHOD LSEC_SSL_METHOD; +#endif + /*--------------------------- Auxiliary Functions ----------------------------*/ /** @@ -42,7 +60,7 @@ static int set_option_flag(const char *opt, unsigned long *flag) /** * Find the protocol. */ -static SSL_METHOD* str2method(const char *method) +static LSEC_SSL_METHOD* str2method(const char *method) { if (!strcmp(method, "sslv3")) return SSLv3_method(); if (!strcmp(method, "tlsv1")) return TLSv1_method(); @@ -84,17 +102,162 @@ static int passwd_cb(char *buf, int size, int flag, void *udata) case LUA_TFUNCTION: lua_pushvalue(L, 3); lua_call(L, 0, 1); - if (lua_type(L, -1) != LUA_TSTRING) + 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; + DH *dh_tmp = NULL; + SSL_CTX *ctx = SSL_get_SSL_CTX(ssl); + L = (lua_State*)SSL_CTX_get_app_data(ctx); + + /* Get the callback */ + luaL_getmetatable(L, "SSL:DH:Registry"); + lua_pushlightuserdata(L, (void*)ctx); + lua_gettable(L, -2); + + /* Invoke the callback */ + lua_pushnumber(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) { + dh_tmp = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); + BIO_free(bio); + } + lua_pop(L, 2); /* Remove values from stack */ + return dh_tmp; +} + +/** + * 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; + + L = (lua_State*)SSL_CTX_get_app_data(ctx); + + /* 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; + 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); + L = (lua_State*)SSL_CTX_get_app_data(ctx); + + /* 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); +} + +static EC_KEY *find_ec_key(const char *str) +{ + p_ec ptr; + for (ptr = curves; ptr->name; ptr++) { + if (!strcmp(str, ptr->name)) + return EC_KEY_new_by_curve_name(ptr->nid); + } + return NULL; +} + /*------------------------------ Lua Functions -------------------------------*/ /** @@ -103,7 +266,7 @@ static int passwd_cb(char *buf, int size, int flag, void *udata) static int create(lua_State *L) { p_context ctx; - SSL_METHOD *method; + LSEC_SSL_METHOD *method; method = str2method(luaL_checkstring(L, 1)); if (!method) { @@ -123,11 +286,15 @@ static int create(lua_State *L) lua_pushstring(L, "error creating context"); return 2; } - ctx->mode = MD_CTX_INVALID; - /* No session support */ - SSL_CTX_set_session_cache_mode(ctx->context, SSL_SESS_CACHE_OFF); + ctx->mode = LSEC_MODE_INVALID; 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 lua_State with the context */ + SSL_CTX_set_app_data(ctx->context, (void*)L); + return 1; } @@ -136,7 +303,7 @@ static int create(lua_State *L) */ static int load_locations(lua_State *L) { - SSL_CTX *ctx = ctx_getcontext(L, 1); + 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) { @@ -154,7 +321,7 @@ static int load_locations(lua_State *L) */ static int load_cert(lua_State *L) { - SSL_CTX *ctx = ctx_getcontext(L, 1); + 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); @@ -172,7 +339,7 @@ static int load_cert(lua_State *L) static int load_key(lua_State *L) { int ret = 1; - SSL_CTX *ctx = ctx_getcontext(L, 1); + SSL_CTX *ctx = lsec_checkcontext(L, 1); const char *filename = luaL_checkstring(L, 2); switch (lua_type(L, 3)) { case LUA_TSTRING: @@ -204,7 +371,7 @@ static int load_key(lua_State *L) */ static int set_cipher(lua_State *L) { - SSL_CTX *ctx = ctx_getcontext(L, 1); + 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); @@ -221,7 +388,7 @@ static int set_cipher(lua_State *L) */ static int set_depth(lua_State *L) { - SSL_CTX *ctx = ctx_getcontext(L, 1); + SSL_CTX *ctx = lsec_checkcontext(L, 1); SSL_CTX_set_verify_depth(ctx, luaL_checkint(L, 2)); lua_pushboolean(L, 1); return 1; @@ -233,20 +400,25 @@ static int set_depth(lua_State *L) static int set_verify(lua_State *L) { int i; + const char *str; int flag = 0; - SSL_CTX *ctx = ctx_getcontext(L, 1); + SSL_CTX *ctx = lsec_checkcontext(L, 1); int max = lua_gettop(L); - /* any flag? */ - if (max > 1) { - for (i = 2; i <= max; i++) { - if (!set_verify_flag(luaL_checkstring(L, i), &flag)) { - lua_pushboolean(L, 0); - lua_pushstring(L, "invalid verify option"); - return 2; - } + for (i = 2; i <= max; i++) { + str = luaL_checkstring(L, i); +#if !defined(SSL_OP_NO_COMPRESSION) && (OPENSSL_VERSION_NUMBER >= 0x0090800f) && (OPENSSL_VERSION_NUMBER < 0x1000000fL) + /* Version 0.9.8 has a different way to disable compression */ + if (!strcmp(luaL_checkstring(L, i), "no_compression")) + ctx->comp_methods = NULL; + else +#endif + if (!set_verify_flag(str, &flag)) { + lua_pushboolean(L, 0); + lua_pushstring(L, "invalid verify option"); + return 2; } - SSL_CTX_set_verify(ctx, flag, NULL); } + if (flag) SSL_CTX_set_verify(ctx, flag, NULL); lua_pushboolean(L, 1); return 1; } @@ -258,7 +430,7 @@ static int set_options(lua_State *L) { int i; unsigned long flag = 0L; - SSL_CTX *ctx = ctx_getcontext(L, 1); + SSL_CTX *ctx = lsec_checkcontext(L, 1); int max = lua_gettop(L); /* any option? */ if (max > 1) { @@ -283,12 +455,12 @@ 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 = MD_CTX_SERVER; + ctx->mode = LSEC_MODE_SERVER; lua_pushboolean(L, 1); return 1; } - if(!strcmp("client", str)) { - ctx->mode = MD_CTX_CLIENT; + if (!strcmp("client", str)) { + ctx->mode = LSEC_MODE_CLIENT; lua_pushboolean(L, 1); return 1; } @@ -298,29 +470,68 @@ static int set_mode(lua_State *L) } /** - * Return a pointer to SSL_CTX structure. + * Configure DH parameters. */ -static int raw_ctx(lua_State *L) +static int set_dhparam(lua_State *L) { - p_context ctx = checkctx(L, 1); - lua_pushlightuserdata(L, (void*)ctx->context); + 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; +} + +/** + * Set elliptic curve. + */ +#ifdef OPENSSL_NO_ECDH +static int set_curve(lua_State *L) +{ + lua_pushboolean(L, 0); + lua_pushstring(L, "OpenSSL does not support ECDH"); + return 2; +} +#else +static int set_curve(lua_State *L) +{ + SSL_CTX *ctx = lsec_checkcontext(L, 1); + const char *str = luaL_checkstring(L, 2); + EC_KEY *key = find_ec_key(str); + if (!key) { + lua_pushboolean(L, 0); + lua_pushstring(L, "elliptic curve not supported"); + return 2; + } + if (!SSL_CTX_set_tmp_ecdh(ctx, key)) { + lua_pushboolean(L, 0); + lua_pushstring(L, "error setting elliptic curve"); + return 2; + } + lua_pushboolean(L, 1); return 1; } +#endif /** * Package functions */ static luaL_Reg funcs[] = { - {"create", create}, - {"locations", load_locations}, - {"loadcert", load_cert}, - {"loadkey", load_key}, - {"setcipher", set_cipher}, - {"setdepth", set_depth}, - {"setverify", set_verify}, - {"setoptions", set_options}, - {"setmode", set_mode}, - {"rawcontext", raw_ctx}, + {"create", create}, + {"locations", load_locations}, + {"loadcert", load_cert}, + {"loadkey", load_key}, + {"setcipher", set_cipher}, + {"setdepth", set_depth}, + {"setdhparam", set_dhparam}, + {"setcurve", set_curve}, + {"setverify", set_verify}, + {"setoptions", set_options}, + {"setmode", set_mode}, {NULL, NULL} }; @@ -333,6 +544,16 @@ 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); + SSL_CTX_free(ctx->context); ctx->context = NULL; } @@ -349,6 +570,60 @@ static int meth_tostring(lua_State *L) 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_pushstring(L, "invalid verify option"); + 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. */ @@ -358,13 +633,21 @@ static luaL_Reg meta[] = { {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* ctx_getcontext(lua_State *L, int idx) +SSL_CTX* lsec_checkcontext(lua_State *L, int idx) { p_context ctx = checkctx(L, idx); return ctx->context; @@ -373,7 +656,7 @@ SSL_CTX* ctx_getcontext(lua_State *L, int idx) /** * Retrieve the mode from the context in the Lua stack. */ -char ctx_getmode(lua_State *L, int idx) +int lsec_getmode(lua_State *L, int idx) { p_context ctx = checkctx(L, idx); return ctx->mode; @@ -384,10 +667,39 @@ char ctx_getmode(lua_State *L, int idx) /** * Registre the module. */ -int luaopen_ssl_context(lua_State *L) +#if (LUA_VERSION_NUM == 501) +LSEC_API int luaopen_ssl_context(lua_State *L) { + luaL_newmetatable(L, "SSL:DH:Registry"); /* Keep all DH callbacks */ + luaL_newmetatable(L, "SSL:Verify:Registry"); /* Keep all verify flags */ luaL_newmetatable(L, "SSL:Context"); luaL_register(L, NULL, meta); + + /* Create __index metamethods for context */ + lua_newtable(L); + luaL_register(L, NULL, meta_index); + lua_setfield(L, -2, "__index"); + + /* Register the module */ luaL_register(L, "ssl.context", funcs); return 1; } +#else +LSEC_API int luaopen_ssl_context(lua_State *L) +{ + luaL_newmetatable(L, "SSL:DH:Registry"); /* Keep all DH callbacks */ + luaL_newmetatable(L, "SSL:Verify:Registry"); /* Keep all verify flags */ + luaL_newmetatable(L, "SSL:Context"); + luaL_setfuncs(L, meta, 0); + + /* Create __index metamethods for context */ + lua_newtable(L); + luaL_setfuncs(L, meta_index, 0); + lua_setfield(L, -2, "__index"); + + /* Return the module */ + lua_newtable(L); + luaL_setfuncs(L, funcs, 0); + return 1; +} +#endif diff --git a/src/context.h b/src/context.h index 902311a..8521852 100644 --- a/src/context.h +++ b/src/context.h @@ -1,5 +1,5 @@ -#ifndef __CONTEXT_H__ -#define __CONTEXT_H__ +#ifndef LSEC_CONTEXT_H +#define LSEC_CONTEXT_H /*-------------------------------------------------------------------------- * LuaSec 0.4.1 @@ -10,28 +10,28 @@ #include #include -#if defined(_WIN32) -#define LUASEC_API __declspec(dllexport) -#else -#define LUASEC_API extern -#endif +#include "config.h" -#define MD_CTX_INVALID 0 -#define MD_CTX_SERVER 1 -#define MD_CTX_CLIENT 2 +#define LSEC_MODE_INVALID 0 +#define LSEC_MODE_SERVER 1 +#define LSEC_MODE_CLIENT 2 + +#define LSEC_VERIFY_CONTINUE 1 +#define LSEC_VERIFY_IGNORE_PURPOSE 2 typedef struct t_context_ { SSL_CTX *context; - char mode; + int mode; } t_context; typedef t_context* p_context; /* Retrieve the SSL context from the Lua stack */ -SSL_CTX *ctx_getcontext(lua_State *L, int idx); +SSL_CTX *lsec_checkcontext(lua_State *L, int idx); + /* Retrieve the mode from the context in the Lua stack */ -char ctx_getmode(lua_State *L, int idx); +int lsec_getmode(lua_State *L, int idx); /* Registre the module. */ -LUASEC_API int luaopen_ssl_context(lua_State *L); +LSEC_API int luaopen_ssl_context(lua_State *L); #endif diff --git a/src/ec.h b/src/ec.h new file mode 100644 index 0000000..e343c4c --- /dev/null +++ b/src/ec.h @@ -0,0 +1,58 @@ +#ifndef LSEC_EC_H +#define LSEC_EC_H + +#include + +typedef struct t_ec_ { + char *name; + int nid; +} t_ec; +typedef t_ec* p_ec; + +/* Elliptic curves supported */ +static t_ec curves[] = { + /* SECG */ + {"secp112r1", NID_secp112r1}, + {"secp112r2", NID_secp112r2}, + {"secp128r1", NID_secp128r1}, + {"secp128r2", NID_secp128r2}, + {"secp160k1", NID_secp160k1}, + {"secp160r1", NID_secp160r1}, + {"secp160r2", NID_secp160r2}, + {"secp192k1", NID_secp192k1}, + {"secp224k1", NID_secp224k1}, + {"secp224r1", NID_secp224r1}, + {"secp256k1", NID_secp256k1}, + {"secp384r1", NID_secp384r1}, + {"secp521r1", NID_secp521r1}, + {"sect113r1", NID_sect113r1}, + {"sect113r2", NID_sect113r2}, + {"sect131r1", NID_sect131r1}, + {"sect131r2", NID_sect131r2}, + {"sect163k1", NID_sect163k1}, + {"sect163r1", NID_sect163r1}, + {"sect163r2", NID_sect163r2}, + {"sect193r1", NID_sect193r1}, + {"sect193r2", NID_sect193r2}, + {"sect233k1", NID_sect233k1}, + {"sect233r1", NID_sect233r1}, + {"sect239k1", NID_sect239k1}, + {"sect283k1", NID_sect283k1}, + {"sect283r1", NID_sect283r1}, + {"sect409k1", NID_sect409k1}, + {"sect409r1", NID_sect409r1}, + {"sect571k1", NID_sect571k1}, + {"sect571r1", NID_sect571r1}, + /* ANSI X9.62 */ + {"prime192v1", NID_X9_62_prime192v1}, + {"prime192v2", NID_X9_62_prime192v2}, + {"prime192v3", NID_X9_62_prime192v3}, + {"prime239v1", NID_X9_62_prime239v1}, + {"prime239v2", NID_X9_62_prime239v2}, + {"prime239v3", NID_X9_62_prime239v3}, + {"prime256v1", NID_X9_62_prime256v1}, + /* End */ + {NULL, 0U} +}; + +#endif diff --git a/src/luasocket/Makefile b/src/luasocket/Makefile new file mode 100644 index 0000000..901c678 --- /dev/null +++ b/src/luasocket/Makefile @@ -0,0 +1,24 @@ +OBJS= \ + io.o \ + buffer.o \ + timeout.o \ + usocket.o + +CC=gcc +CFLAGS=$(MYCFLAGS) -DLUASOCKET_DEBUG + +.PHONY: all clean + +all: libluasocket.a + +libluasocket.a: $(OBJS) + ar rcu $@ $(OBJS) + ranlib $@ + +clean: + rm -f $(OBJS) libluasocket.a + +buffer.o: buffer.c buffer.h io.h timeout.h +io.o: io.c io.h timeout.h +timeout.o: timeout.c timeout.h +usocket.o: usocket.c socket.h io.h timeout.h usocket.h diff --git a/src/luasocket/buffer.c b/src/luasocket/buffer.c new file mode 100644 index 0000000..0215e59 --- /dev/null +++ b/src/luasocket/buffer.c @@ -0,0 +1,237 @@ +/*=========================================================================*\ +* LuaSocket 2.0.2 +* Copyright (C) 2004-2007 Diego Nehab +* +* Input/Output interface for Lua programs +* +* RCS ID: $Id: buffer.c,v 1.28 2007/06/11 23:44:54 diego Exp $ +\*=========================================================================*/ +#include "lua.h" +#include "lauxlib.h" + +#include "buffer.h" + +/*=========================================================================*\ +* Internal function prototypes +\*=========================================================================*/ +static int recvraw(p_buffer buf, size_t wanted, luaL_Buffer *b); +static int recvline(p_buffer buf, luaL_Buffer *b); +static int recvall(p_buffer buf, luaL_Buffer *b); +static int buffer_get(p_buffer buf, const char **data, size_t *count); +static void buffer_skip(p_buffer buf, size_t count); +static int sendraw(p_buffer buf, const char *data, size_t count, size_t *sent); + +/* min and max macros */ +#ifndef MIN +#define MIN(x, y) ((x) < (y) ? x : y) +#endif +#ifndef MAX +#define MAX(x, y) ((x) > (y) ? x : y) +#endif + +/*=========================================================================*\ +* Exported functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Initializes C structure +\*-------------------------------------------------------------------------*/ +void buffer_init(p_buffer buf, p_io io, p_timeout tm) { + buf->first = buf->last = 0; + buf->io = io; + buf->tm = tm; +} + +/*-------------------------------------------------------------------------*\ +* object:send() interface +\*-------------------------------------------------------------------------*/ +int buffer_meth_send(lua_State *L, p_buffer buf) { + int top = lua_gettop(L); + int err = IO_DONE; + size_t size = 0, sent = 0; + const char *data = luaL_checklstring(L, 2, &size); + long start = (long) luaL_optnumber(L, 3, 1); + long end = (long) luaL_optnumber(L, 4, -1); + p_timeout tm = timeout_markstart(buf->tm); + if (start < 0) start = (long) (size+start+1); + if (end < 0) end = (long) (size+end+1); + if (start < 1) start = (long) 1; + if (end > (long) size) end = (long) size; + if (start <= end) err = sendraw(buf, data+start-1, end-start+1, &sent); + /* check if there was an error */ + if (err != IO_DONE) { + lua_pushnil(L); + lua_pushstring(L, buf->io->error(buf->io->ctx, err)); + lua_pushnumber(L, sent+start-1); + } else { + lua_pushnumber(L, sent+start-1); + lua_pushnil(L); + lua_pushnil(L); + } +#ifdef LUASOCKET_DEBUG + /* push time elapsed during operation as the last return value */ + lua_pushnumber(L, timeout_gettime() - timeout_getstart(tm)); +#endif + return lua_gettop(L) - top; +} + +/*-------------------------------------------------------------------------*\ +* object:receive() interface +\*-------------------------------------------------------------------------*/ +int buffer_meth_receive(lua_State *L, p_buffer buf) { + int err = IO_DONE, top = lua_gettop(L); + luaL_Buffer b; + size_t size; + const char *part = luaL_optlstring(L, 3, "", &size); + p_timeout tm = timeout_markstart(buf->tm); + /* initialize buffer with optional extra prefix + * (useful for concatenating previous partial results) */ + luaL_buffinit(L, &b); + luaL_addlstring(&b, part, size); + /* receive new patterns */ + if (!lua_isnumber(L, 2)) { + const char *p= luaL_optstring(L, 2, "*l"); + if (p[0] == '*' && p[1] == 'l') err = recvline(buf, &b); + else if (p[0] == '*' && p[1] == 'a') err = recvall(buf, &b); + else luaL_argcheck(L, 0, 2, "invalid receive pattern"); + /* get a fixed number of bytes (minus what was already partially + * received) */ + } else err = recvraw(buf, (size_t) lua_tonumber(L, 2)-size, &b); + /* check if there was an error */ + if (err != IO_DONE) { + /* we can't push anyting in the stack before pushing the + * contents of the buffer. this is the reason for the complication */ + luaL_pushresult(&b); + lua_pushstring(L, buf->io->error(buf->io->ctx, err)); + lua_pushvalue(L, -2); + lua_pushnil(L); + lua_replace(L, -4); + } else { + luaL_pushresult(&b); + lua_pushnil(L); + lua_pushnil(L); + } +#ifdef LUASOCKET_DEBUG + /* push time elapsed during operation as the last return value */ + lua_pushnumber(L, timeout_gettime() - timeout_getstart(tm)); +#endif + return lua_gettop(L) - top; +} + +/*-------------------------------------------------------------------------*\ +* Determines if there is any data in the read buffer +\*-------------------------------------------------------------------------*/ +int buffer_isempty(p_buffer buf) { + return buf->first >= buf->last; +} + +/*=========================================================================*\ +* Internal functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Sends a block of data (unbuffered) +\*-------------------------------------------------------------------------*/ +#define STEPSIZE 8192 +static int sendraw(p_buffer buf, const char *data, size_t count, size_t *sent) { + p_io io = buf->io; + p_timeout tm = buf->tm; + size_t total = 0; + int err = IO_DONE; + while (total < count && err == IO_DONE) { + size_t done; + size_t step = (count-total <= STEPSIZE)? count-total: STEPSIZE; + err = io->send(io->ctx, data+total, step, &done, tm); + total += done; + } + *sent = total; + return err; +} + +/*-------------------------------------------------------------------------*\ +* Reads a fixed number of bytes (buffered) +\*-------------------------------------------------------------------------*/ +static int recvraw(p_buffer buf, size_t wanted, luaL_Buffer *b) { + int err = IO_DONE; + size_t total = 0; + while (err == IO_DONE) { + size_t count; const char *data; + err = buffer_get(buf, &data, &count); + count = MIN(count, wanted - total); + luaL_addlstring(b, data, count); + buffer_skip(buf, count); + total += count; + if (total >= wanted) break; + } + return err; +} + +/*-------------------------------------------------------------------------*\ +* Reads everything until the connection is closed (buffered) +\*-------------------------------------------------------------------------*/ +static int recvall(p_buffer buf, luaL_Buffer *b) { + int err = IO_DONE; + size_t total = 0; + while (err == IO_DONE) { + const char *data; size_t count; + err = buffer_get(buf, &data, &count); + total += count; + luaL_addlstring(b, data, count); + buffer_skip(buf, count); + } + if (err == IO_CLOSED) { + if (total > 0) return IO_DONE; + else return IO_CLOSED; + } else return err; +} + +/*-------------------------------------------------------------------------*\ +* Reads a line terminated by a CR LF pair or just by a LF. The CR and LF +* are not returned by the function and are discarded from the buffer +\*-------------------------------------------------------------------------*/ +static int recvline(p_buffer buf, luaL_Buffer *b) { + int err = IO_DONE; + while (err == IO_DONE) { + size_t count, pos; const char *data; + err = buffer_get(buf, &data, &count); + pos = 0; + while (pos < count && data[pos] != '\n') { + /* we ignore all \r's */ + if (data[pos] != '\r') luaL_addchar(b, data[pos]); + pos++; + } + if (pos < count) { /* found '\n' */ + buffer_skip(buf, pos+1); /* skip '\n' too */ + break; /* we are done */ + } else /* reached the end of the buffer */ + buffer_skip(buf, pos); + } + return err; +} + +/*-------------------------------------------------------------------------*\ +* Skips a given number of bytes from read buffer. No data is read from the +* transport layer +\*-------------------------------------------------------------------------*/ +static void buffer_skip(p_buffer buf, size_t count) { + buf->first += count; + if (buffer_isempty(buf)) + buf->first = buf->last = 0; +} + +/*-------------------------------------------------------------------------*\ +* Return any data available in buffer, or get more data from transport layer +* if buffer is empty +\*-------------------------------------------------------------------------*/ +static int buffer_get(p_buffer buf, const char **data, size_t *count) { + int err = IO_DONE; + p_io io = buf->io; + p_timeout tm = buf->tm; + if (buffer_isempty(buf)) { + size_t got; + err = io->recv(io->ctx, buf->data, BUF_SIZE, &got, tm); + buf->first = 0; + buf->last = got; + } + *count = buf->last - buf->first; + *data = buf->data + buf->first; + return err; +} diff --git a/src/luasocket/buffer.h b/src/luasocket/buffer.h new file mode 100644 index 0000000..61d2798 --- /dev/null +++ b/src/luasocket/buffer.h @@ -0,0 +1,45 @@ +#ifndef BUF_H +#define BUF_H +/*=========================================================================*\ +* LuaSocket 2.0.2 +* Copyright (C) 2004-2007 Diego Nehab +* +* Input/Output interface for Lua programs +* +* Line patterns require buffering. Reading one character at a time involves +* too many system calls and is very slow. This module implements the +* LuaSocket interface for input/output on connected objects, as seen by +* Lua programs. +* +* Input is buffered. Output is *not* buffered because there was no simple +* way of making sure the buffered output data would ever be sent. +* +* The module is built on top of the I/O abstraction defined in io.h and the +* timeout management is done with the timeout.h interface. +* +* +* RCS ID: $Id: buffer.h,v 1.12 2005/10/07 04:40:59 diego Exp $ +\*=========================================================================*/ +#include + +#include "io.h" +#include "timeout.h" + +/* buffer size in bytes */ +#define BUF_SIZE 8192 + +/* buffer control structure */ +typedef struct t_buffer_ { + p_io io; /* IO driver used for this buffer */ + p_timeout tm; /* timeout management for this buffer */ + size_t first, last; /* index of first and last bytes of stored data */ + char data[BUF_SIZE]; /* storage space for buffer data */ +} t_buffer; +typedef t_buffer *p_buffer; + +void buffer_init(p_buffer buf, p_io io, p_timeout tm); +int buffer_meth_send(lua_State *L, p_buffer buf); +int buffer_meth_receive(lua_State *L, p_buffer buf); +int buffer_isempty(p_buffer buf); + +#endif /* BUF_H */ diff --git a/src/luasocket/io.c b/src/luasocket/io.c new file mode 100644 index 0000000..ff2e7ad --- /dev/null +++ b/src/luasocket/io.c @@ -0,0 +1,34 @@ +/*=========================================================================*\ +* LuaSocket 2.0.2 +* Copyright (C) 2004-2007 Diego Nehab +* +* Input/Output abstraction +* +* RCS ID: $Id: io.c 2 2006-04-30 19:30:47Z brunoos $ +\*=========================================================================*/ +#include "io.h" + +/*=========================================================================*\ +* Exported functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Initializes C structure +\*-------------------------------------------------------------------------*/ +void io_init(p_io io, p_send send, p_recv recv, p_error error, void *ctx) { + io->send = send; + io->recv = recv; + io->error = error; + io->ctx = ctx; +} + +/*-------------------------------------------------------------------------*\ +* I/O error strings +\*-------------------------------------------------------------------------*/ +const char *io_strerror(int err) { + switch (err) { + case IO_DONE: return NULL; + case IO_CLOSED: return "closed"; + case IO_TIMEOUT: return "timeout"; + default: return "unknown error"; + } +} diff --git a/src/luasocket/io.h b/src/luasocket/io.h new file mode 100644 index 0000000..b5942d3 --- /dev/null +++ b/src/luasocket/io.h @@ -0,0 +1,70 @@ +#ifndef IO_H +#define IO_H +/*=========================================================================*\ +* LuaSocket 2.0.2 +* Copyright (C) 2004-2007 Diego Nehab +* +* Input/Output abstraction +* +* This module defines the interface that LuaSocket expects from the +* transport layer for streamed input/output. The idea is that if any +* transport implements this interface, then the buffer.c functions +* automatically work on it. +* +* The module socket.h implements this interface, and thus the module tcp.h +* is very simple. +* +* RCS ID: $Id: io.h 6 2006-04-30 20:33:05Z brunoos $ +\*=========================================================================*/ +#include +#include + +#include "timeout.h" + +/* IO error codes */ +enum { + IO_DONE = 0, /* operation completed successfully */ + IO_TIMEOUT = -1, /* operation timed out */ + IO_CLOSED = -2, /* the connection has been closed */ + IO_UNKNOWN = -3, /* Unknown error */ + IO_SSL = -4 /* SSL error */ +}; + +/* interface to error message function */ +typedef const char *(*p_error) ( + void *ctx, /* context needed by send */ + int err /* error code */ +); + +/* interface to send function */ +typedef int (*p_send) ( + void *ctx, /* context needed by send */ + const char *data, /* pointer to buffer with data to send */ + size_t count, /* number of bytes to send from buffer */ + size_t *sent, /* number of bytes sent uppon return */ + p_timeout tm /* timeout control */ +); + +/* interface to recv function */ +typedef int (*p_recv) ( + void *ctx, /* context needed by recv */ + char *data, /* pointer to buffer where data will be writen */ + size_t count, /* number of bytes to receive into buffer */ + size_t *got, /* number of bytes received uppon return */ + p_timeout tm /* timeout control */ +); + +/* IO driver definition */ +typedef struct t_io_ { + void *ctx; /* context needed by send/recv */ + p_send send; /* send function pointer */ + p_recv recv; /* receive function pointer */ + p_error error; /* strerror function */ +} t_io; +typedef t_io *p_io; + +void io_init(p_io io, p_send send, p_recv recv, p_error error, void *ctx); +const char *io_strerror(int err); + +#endif /* IO_H */ + diff --git a/src/luasocket/socket.h b/src/luasocket/socket.h new file mode 100644 index 0000000..8a5b1f2 --- /dev/null +++ b/src/luasocket/socket.h @@ -0,0 +1,47 @@ +#ifndef SOCKET_H +#define SOCKET_H +/*=========================================================================*\ +* LuaSocket 2.0.2 +* Copyright (C) 2004-2007 Diego Nehab +* +* Socket compatibilization module +* +* BSD Sockets and WinSock are similar, but there are a few irritating +* differences. Also, not all *nix platforms behave the same. This module +* (and the associated usocket.h and wsocket.h) factor these differences and +* creates a interface compatible with the io.h module. +* +* RCS ID: $Id: socket.h 2 2006-04-30 19:30:47Z brunoos $ +\*=========================================================================*/ +#include "io.h" + +/*=========================================================================*\ +* Platform specific compatibilization +\*=========================================================================*/ +#ifdef _WIN32 +#include "wsocket.h" +#else +#include "usocket.h" +#endif + +/*=========================================================================*\ +* The connect and accept functions accept a timeout and their +* implementations are somewhat complicated. We chose to move +* the timeout control into this module for these functions in +* order to simplify the modules that use them. +\*=========================================================================*/ +#include "timeout.h" + +/*=========================================================================*\ +* Functions bellow implement a comfortable platform independent +* interface to sockets +\*=========================================================================*/ +int socket_open(void); +int socket_close(void); +void socket_destroy(p_socket ps); +void socket_setnonblocking(p_socket ps); +void socket_setblocking(p_socket ps); +int socket_waitfd(p_socket ps, int sw, p_timeout tm); +const char *socket_strerror(int err); + +#endif /* SOCKET_H */ diff --git a/src/luasocket/timeout.c b/src/luasocket/timeout.c new file mode 100644 index 0000000..d141213 --- /dev/null +++ b/src/luasocket/timeout.c @@ -0,0 +1,155 @@ +/*=========================================================================*\ +* LuaSocket 2.0.2 +* Copyright (C) 2004-2007 Diego Nehab +* +* Timeout management functions +* +* RCS ID: $Id: timeout.c,v 1.30 2005/10/07 04:40:59 diego Exp $ +\*=========================================================================*/ +#include + +#ifdef _WIN32 +#include +#else +#include +#include +#endif + +#include +#include + +#include "timeout.h" + +/* min and max macros */ +#ifndef MIN +#define MIN(x, y) ((x) < (y) ? x : y) +#endif +#ifndef MAX +#define MAX(x, y) ((x) > (y) ? x : y) +#endif + +/*=========================================================================*\ +* Exported functions. +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Initialize structure +\*-------------------------------------------------------------------------*/ +void timeout_init(p_timeout tm, double block, double total) { + tm->block = block; + tm->total = total; +} + +/*-------------------------------------------------------------------------*\ +* Determines how much time we have left for the next system call, +* if the previous call was successful +* Input +* tm: timeout control structure +* Returns +* the number of ms left or -1 if there is no time limit +\*-------------------------------------------------------------------------*/ +double timeout_get(p_timeout tm) { + if (tm->block < 0.0 && tm->total < 0.0) { + return -1; + } else if (tm->block < 0.0) { + double t = tm->total - timeout_gettime() + tm->start; + return MAX(t, 0.0); + } else if (tm->total < 0.0) { + return tm->block; + } else { + double t = tm->total - timeout_gettime() + tm->start; + return MIN(tm->block, MAX(t, 0.0)); + } +} + +/*-------------------------------------------------------------------------*\ +* Returns time since start of operation +* Input +* tm: timeout control structure +* Returns +* start field of structure +\*-------------------------------------------------------------------------*/ +double timeout_getstart(p_timeout tm) { + return tm->start; +} + +/*-------------------------------------------------------------------------*\ +* Determines how much time we have left for the next system call, +* if the previous call was a failure +* Input +* tm: timeout control structure +* Returns +* the number of ms left or -1 if there is no time limit +\*-------------------------------------------------------------------------*/ +double timeout_getretry(p_timeout tm) { + if (tm->block < 0.0 && tm->total < 0.0) { + return -1; + } else if (tm->block < 0.0) { + double t = tm->total - timeout_gettime() + tm->start; + return MAX(t, 0.0); + } else if (tm->total < 0.0) { + double t = tm->block - timeout_gettime() + tm->start; + return MAX(t, 0.0); + } else { + double t = tm->total - timeout_gettime() + tm->start; + return MIN(tm->block, MAX(t, 0.0)); + } +} + +/*-------------------------------------------------------------------------*\ +* Marks the operation start time in structure +* Input +* tm: timeout control structure +\*-------------------------------------------------------------------------*/ +p_timeout timeout_markstart(p_timeout tm) { + tm->start = timeout_gettime(); + return tm; +} + +/*-------------------------------------------------------------------------*\ +* Gets time in s, relative to January 1, 1970 (UTC) +* Returns +* time in s. +\*-------------------------------------------------------------------------*/ +#ifdef _WIN32 +double timeout_gettime(void) { + FILETIME ft; + double t; + GetSystemTimeAsFileTime(&ft); + /* Windows file time (time since January 1, 1601 (UTC)) */ + t = ft.dwLowDateTime/1.0e7 + ft.dwHighDateTime*(4294967296.0/1.0e7); + /* convert to Unix Epoch time (time since January 1, 1970 (UTC)) */ + return (t - 11644473600.0); +} +#else +double timeout_gettime(void) { + struct timeval v; + gettimeofday(&v, (struct timezone *) NULL); + /* Unix Epoch time (time since January 1, 1970 (UTC)) */ + return v.tv_sec + v.tv_usec/1.0e6; +} +#endif + +/*-------------------------------------------------------------------------*\ +* Sets timeout values for IO operations +* Lua Input: base, time [, mode] +* time: time out value in seconds +* mode: "b" for block timeout, "t" for total timeout. (default: b) +\*-------------------------------------------------------------------------*/ +int timeout_meth_settimeout(lua_State *L, p_timeout tm) { + double t = luaL_optnumber(L, 2, -1); + const char *mode = luaL_optstring(L, 3, "b"); + switch (*mode) { + case 'b': + tm->block = t; + break; + case 'r': case 't': + tm->total = t; + break; + default: + luaL_argcheck(L, 0, 3, "invalid timeout mode"); + break; + } + lua_pushnumber(L, 1); + return 1; +} + diff --git a/src/luasocket/timeout.h b/src/luasocket/timeout.h new file mode 100644 index 0000000..1157d43 --- /dev/null +++ b/src/luasocket/timeout.h @@ -0,0 +1,32 @@ +#ifndef TIMEOUT_H +#define TIMEOUT_H +/*=========================================================================*\ +* LuaSocket 2.0.2 +* Copyright (C) 2004-2007 Diego Nehab +* +* Timeout management functions +* +* RCS ID: $Id: timeout.h 2 2006-04-30 19:30:47Z brunoos $ +\*=========================================================================*/ +#include + +/* timeout control structure */ +typedef struct t_timeout_ { + double block; /* maximum time for blocking calls */ + double total; /* total number of miliseconds for operation */ + double start; /* time of start of operation */ +} t_timeout; +typedef t_timeout *p_timeout; + +int timeout_open(lua_State *L); +void timeout_init(p_timeout tm, double block, double total); +double timeout_get(p_timeout tm); +double timeout_getretry(p_timeout tm); +p_timeout timeout_markstart(p_timeout tm); +double timeout_getstart(p_timeout tm); +double timeout_gettime(void); +int timeout_meth_settimeout(lua_State *L, p_timeout tm); + +#define timeout_iszero(tm) ((tm)->block == 0.0) + +#endif /* TIMEOUT_H */ diff --git a/src/luasocket/usocket.c b/src/luasocket/usocket.c new file mode 100644 index 0000000..21c1c36 --- /dev/null +++ b/src/luasocket/usocket.c @@ -0,0 +1,136 @@ +/*=========================================================================*\ +* LuaSocket 2.0.2 +* Copyright (C) 2004-2007 Diego Nehab +* +* Socket compatibilization module for Unix +* +* The code is now interrupt-safe. +* The penalty of calling select to avoid busy-wait is only paid when +* the I/O call fail in the first place. +* +* RCS ID: $Id: usocket.c,v 1.38 2007/10/13 23:55:20 diego Exp $ +\*=========================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "socket.h" +#include "usocket.h" + +/*-------------------------------------------------------------------------*\ +* Wait for readable/writable/connected socket with timeout +\*-------------------------------------------------------------------------*/ +#ifdef SOCKET_POLL +int socket_waitfd(p_socket ps, int sw, p_timeout tm) { + int ret; + struct pollfd pfd; + pfd.fd = *ps; + pfd.events = sw; + pfd.revents = 0; + if (timeout_iszero(tm)) return IO_TIMEOUT; /* optimize timeout == 0 case */ + do { + int t = (int)(timeout_getretry(tm)*1e3); + ret = poll(&pfd, 1, t >= 0? t: -1); + } while (ret == -1 && errno == EINTR); + if (ret == -1) return errno; + if (ret == 0) return IO_TIMEOUT; + if (sw == WAITFD_C && (pfd.revents & (POLLIN|POLLERR))) return IO_CLOSED; + return IO_DONE; +} +#else +int socket_waitfd(p_socket ps, int sw, p_timeout tm) { + int ret; + fd_set rfds, wfds, *rp, *wp; + struct timeval tv, *tp; + double t; + if (timeout_iszero(tm)) return IO_TIMEOUT; /* optimize timeout == 0 case */ + do { + /* must set bits within loop, because select may have modifed them */ + rp = wp = NULL; + if (sw & WAITFD_R) { FD_ZERO(&rfds); FD_SET(*ps, &rfds); rp = &rfds; } + if (sw & WAITFD_W) { FD_ZERO(&wfds); FD_SET(*ps, &wfds); wp = &wfds; } + t = timeout_getretry(tm); + tp = NULL; + if (t >= 0.0) { + tv.tv_sec = (int)t; + tv.tv_usec = (int)((t-tv.tv_sec)*1.0e6); + tp = &tv; + } + ret = select(*ps+1, rp, wp, NULL, tp); + } while (ret == -1 && errno == EINTR); + if (ret == -1) return errno; + if (ret == 0) return IO_TIMEOUT; + if (sw == WAITFD_C && FD_ISSET(*ps, &rfds)) return IO_CLOSED; + return IO_DONE; +} +#endif + + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int socket_open(void) { + /* instals a handler to ignore sigpipe or it will crash us */ + signal(SIGPIPE, SIG_IGN); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Close module +\*-------------------------------------------------------------------------*/ +int socket_close(void) { + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Close and inutilize socket +\*-------------------------------------------------------------------------*/ +void socket_destroy(p_socket ps) { + if (*ps != SOCKET_INVALID) { + socket_setblocking(ps); + close(*ps); + *ps = SOCKET_INVALID; + } +} + +/*-------------------------------------------------------------------------*\ +* Put socket into blocking mode +\*-------------------------------------------------------------------------*/ +void socket_setblocking(p_socket ps) { + int flags = fcntl(*ps, F_GETFL, 0); + flags &= (~(O_NONBLOCK)); + fcntl(*ps, F_SETFL, flags); +} + +/*-------------------------------------------------------------------------*\ +* Put socket into non-blocking mode +\*-------------------------------------------------------------------------*/ +void socket_setnonblocking(p_socket ps) { + int flags = fcntl(*ps, F_GETFL, 0); + flags |= O_NONBLOCK; + fcntl(*ps, F_SETFL, flags); +} + +/*-------------------------------------------------------------------------*\ +* Error translation functions +* Make sure important error messages are standard +\*-------------------------------------------------------------------------*/ +const char *socket_strerror(int err) { + if (err <= 0) return io_strerror(err); + switch (err) { + case EADDRINUSE: return "address already in use"; + case EISCONN: return "already connected"; + case EACCES: return "permission denied"; + case ECONNREFUSED: return "connection refused"; + case ECONNABORTED: return "closed"; + case ECONNRESET: return "closed"; + case EPIPE: return "closed"; + case ETIMEDOUT: return "timeout"; + default: return strerror(errno); + } +} diff --git a/src/luasocket/usocket.h b/src/luasocket/usocket.h new file mode 100644 index 0000000..de8a4a9 --- /dev/null +++ b/src/luasocket/usocket.h @@ -0,0 +1,28 @@ +#ifndef USOCKET_H +#define USOCKET_H +/*=========================================================================*\ +* LuaSocket 2.0.2 +* Copyright (C) 2004-2007 Diego Nehab +* +* Socket compatibilization module for Unix +* +* RCS ID: $Id: usocket.h 6 2006-04-30 20:33:05Z brunoos $ +\*=========================================================================*/ + +#ifdef SOCKET_POLL +#include +#define WAITFD_R POLLIN +#define WAITFD_W POLLOUT +#define WAITFD_C (POLLIN|POLLOUT) +#else +#define WAITFD_R 1 +#define WAITFD_W 2 +#define WAITFD_C (WAITFD_R|WAITFD_W) +#endif + +typedef int t_socket; +typedef t_socket *p_socket; + +#define SOCKET_INVALID (-1) + +#endif /* USOCKET_H */ diff --git a/src/luasocket/wsocket.c b/src/luasocket/wsocket.c new file mode 100644 index 0000000..c187ee9 --- /dev/null +++ b/src/luasocket/wsocket.c @@ -0,0 +1,162 @@ +/*=========================================================================*\ +* LuaSocket 2.0.2 +* Copyright (C) 2004-2007 Diego Nehab +* +* Socket compatibilization module for Win32 +* +* The penalty of calling select to avoid busy-wait is only paid when +* the I/O call fail in the first place. +* +* RCS ID: $Id: wsocket.c,v 1.36 2007/06/11 23:44:54 diego Exp $ +\*=========================================================================*/ +#include + +#include "socket.h" + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int socket_open(void) { + WSADATA wsaData; + WORD wVersionRequested = MAKEWORD(2, 0); + int err = WSAStartup(wVersionRequested, &wsaData ); + if (err != 0) return 0; + if ((LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 0) && + (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)) { + WSACleanup(); + return 0; + } + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Close module +\*-------------------------------------------------------------------------*/ +int socket_close(void) { + WSACleanup(); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Wait for readable/writable/connected socket with timeout +\*-------------------------------------------------------------------------*/ +int socket_waitfd(p_socket ps, int sw, p_timeout tm) { + int ret; + fd_set rfds, wfds, efds, *rp = NULL, *wp = NULL, *ep = NULL; + struct timeval tv, *tp = NULL; + double t; + if (timeout_iszero(tm)) return IO_TIMEOUT; /* optimize timeout == 0 case */ + if (sw & WAITFD_R) { + FD_ZERO(&rfds); + FD_SET(*ps, &rfds); + rp = &rfds; + } + if (sw & WAITFD_W) { FD_ZERO(&wfds); FD_SET(*ps, &wfds); wp = &wfds; } + if (sw & WAITFD_C) { FD_ZERO(&efds); FD_SET(*ps, &efds); ep = &efds; } + if ((t = timeout_get(tm)) >= 0.0) { + tv.tv_sec = (int) t; + tv.tv_usec = (int) ((t-tv.tv_sec)*1.0e6); + tp = &tv; + } + ret = select(0, rp, wp, ep, tp); + if (ret == -1) return WSAGetLastError(); + if (ret == 0) return IO_TIMEOUT; + if (sw == WAITFD_C && FD_ISSET(*ps, &efds)) return IO_CLOSED; + return IO_DONE; +} + +/*-------------------------------------------------------------------------*\ +* Close and inutilize socket +\*-------------------------------------------------------------------------*/ +void socket_destroy(p_socket ps) { + if (*ps != SOCKET_INVALID) { + socket_setblocking(ps); /* close can take a long time on WIN32 */ + closesocket(*ps); + *ps = SOCKET_INVALID; + } +} + +/*-------------------------------------------------------------------------*\ +* Put socket into blocking mode +\*-------------------------------------------------------------------------*/ +void socket_setblocking(p_socket ps) { + u_long argp = 0; + ioctlsocket(*ps, FIONBIO, &argp); +} + +/*-------------------------------------------------------------------------*\ +* Put socket into non-blocking mode +\*-------------------------------------------------------------------------*/ +void socket_setnonblocking(p_socket ps) { + u_long argp = 1; + ioctlsocket(*ps, FIONBIO, &argp); +} + +/*-------------------------------------------------------------------------*\ +* Error translation functions +\*-------------------------------------------------------------------------*/ + +/* WinSock doesn't have a strerror... */ +static const char *wstrerror(int err) { + switch (err) { + case WSAEINTR: return "Interrupted function call"; + case WSAEACCES: return "Permission denied"; + case WSAEFAULT: return "Bad address"; + case WSAEINVAL: return "Invalid argument"; + case WSAEMFILE: return "Too many open files"; + case WSAEWOULDBLOCK: return "Resource temporarily unavailable"; + case WSAEINPROGRESS: return "Operation now in progress"; + case WSAEALREADY: return "Operation already in progress"; + case WSAENOTSOCK: return "Socket operation on nonsocket"; + case WSAEDESTADDRREQ: return "Destination address required"; + case WSAEMSGSIZE: return "Message too long"; + case WSAEPROTOTYPE: return "Protocol wrong type for socket"; + case WSAENOPROTOOPT: return "Bad protocol option"; + case WSAEPROTONOSUPPORT: return "Protocol not supported"; + case WSAESOCKTNOSUPPORT: return "Socket type not supported"; + case WSAEOPNOTSUPP: return "Operation not supported"; + case WSAEPFNOSUPPORT: return "Protocol family not supported"; + case WSAEAFNOSUPPORT: + return "Address family not supported by protocol family"; + case WSAEADDRINUSE: return "Address already in use"; + case WSAEADDRNOTAVAIL: return "Cannot assign requested address"; + case WSAENETDOWN: return "Network is down"; + case WSAENETUNREACH: return "Network is unreachable"; + case WSAENETRESET: return "Network dropped connection on reset"; + case WSAECONNABORTED: return "Software caused connection abort"; + case WSAECONNRESET: return "Connection reset by peer"; + case WSAENOBUFS: return "No buffer space available"; + case WSAEISCONN: return "Socket is already connected"; + case WSAENOTCONN: return "Socket is not connected"; + case WSAESHUTDOWN: return "Cannot send after socket shutdown"; + case WSAETIMEDOUT: return "Connection timed out"; + case WSAECONNREFUSED: return "Connection refused"; + case WSAEHOSTDOWN: return "Host is down"; + case WSAEHOSTUNREACH: return "No route to host"; + case WSAEPROCLIM: return "Too many processes"; + case WSASYSNOTREADY: return "Network subsystem is unavailable"; + case WSAVERNOTSUPPORTED: return "Winsock.dll version out of range"; + case WSANOTINITIALISED: + return "Successful WSAStartup not yet performed"; + case WSAEDISCON: return "Graceful shutdown in progress"; + case WSAHOST_NOT_FOUND: return "Host not found"; + case WSATRY_AGAIN: return "Nonauthoritative host not found"; + case WSANO_RECOVERY: return "Nonrecoverable name lookup error"; + case WSANO_DATA: return "Valid name, no data record of requested type"; + default: return "Unknown error"; + } +} + +const char *socket_strerror(int err) { + if (err <= 0) return io_strerror(err); + switch (err) { + case WSAEADDRINUSE: return "address already in use"; + case WSAECONNREFUSED: return "connection refused"; + case WSAEISCONN: return "already connected"; + case WSAEACCES: return "permission denied"; + case WSAECONNABORTED: return "closed"; + case WSAECONNRESET: return "closed"; + case WSAETIMEDOUT: return "timeout"; + default: return wstrerror(err); + } +} diff --git a/src/luasocket/wsocket.h b/src/luasocket/wsocket.h new file mode 100644 index 0000000..b977df6 --- /dev/null +++ b/src/luasocket/wsocket.h @@ -0,0 +1,28 @@ +#ifndef WSOCKET_H +#define WSOCKET_H +/*=========================================================================*\ +* LuaSocket 2.0.2 +* Copyright (C) 2004-2007 Diego Nehab +* +* Socket compatibilization module for Win32 +* +* RCS ID: $Id: wsocket.h 2 2006-04-30 19:30:47Z brunoos $ +\*=========================================================================*/ + +/*=========================================================================*\ +* WinSock include files +\*=========================================================================*/ +#include + +#define WAITFD_R 1 +#define WAITFD_W 2 +#define WAITFD_E 4 +#define WAITFD_C (WAITFD_E|WAITFD_W) + +#define SOCKET_INVALID (INVALID_SOCKET) + +typedef int socklen_t; +typedef SOCKET t_socket; +typedef t_socket *p_socket; + +#endif /* WSOCKET_H */ diff --git a/src/options.h b/src/options.h index c260d46..f0daa29 100644 --- a/src/options.h +++ b/src/options.h @@ -1,67 +1,26 @@ +#ifndef LSEC_OPTIONS_H +#define LSEC_OPTIONS_H + /*-------------------------------------------------------------------------- * LuaSec 0.4.1 * Copyright (C) 2006-2011 Bruno Silvestre * *--------------------------------------------------------------------------*/ +#include + +/* If you need to generate these options again, see options.lua */ + +/* + OpenSSL version: OpenSSL 1.0.1 14 Mar 2012 +*/ + struct ssl_option_s { const char *name; unsigned long code; }; typedef struct ssl_option_s ssl_option_t; -/* --- Supported SSL options and script in Lua 5.1 to generate the file. --- Ugly, but easier to maintain. - -local options = [[ -SSL_OP_ALL -SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION -SSL_OP_CIPHER_SERVER_PREFERENCE -SSL_OP_CISCO_ANYCONNECT -SSL_OP_COOKIE_EXCHANGE -SSL_OP_CRYPTOPRO_TLSEXT_BUG -SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS -SSL_OP_EPHEMERAL_RSA -SSL_OP_LEGACY_SERVER_CONNECT -SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER -SSL_OP_MICROSOFT_SESS_ID_BUG -SSL_OP_MSIE_SSLV2_RSA_PADDING -SSL_OP_NETSCAPE_CA_DN_BUG -SSL_OP_NETSCAPE_CHALLENGE_BUG -SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG -SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG -SSL_OP_NO_COMPRESSION -SSL_OP_NO_QUERY_MTU -SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION -SSL_OP_NO_SSLv2 -SSL_OP_NO_SSLv3 -SSL_OP_NO_TICKET -SSL_OP_NO_TLSv1 -SSL_OP_PKCS1_CHECK_1 -SSL_OP_PKCS1_CHECK_2 -SSL_OP_SINGLE_DH_USE -SSL_OP_SINGLE_ECDH_USE -SSL_OP_SSLEAY_080_CLIENT_DH_BUG -SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG -SSL_OP_TLS_BLOCK_PADDING_BUG -SSL_OP_TLS_D5_BUG -SSL_OP_TLS_ROLLBACK_BUG -]] - -print([[static ssl_option_t ssl_options[] = {]]) - -for option in string.gmatch(options, "(%S+)") do - local name = string.lower(string.sub(option, 8)) - print(string.format([[#if defined(%s)]], option)) - print(string.format([[ {"%s", %s},]], name, option)) - print([[#endif]]) -end - -print([[ {NULL, 0L}]]) -print([[};]]) -*/ - static ssl_option_t ssl_options[] = { #if defined(SSL_OP_ALL) {"all", SSL_OP_ALL}, @@ -132,6 +91,12 @@ static ssl_option_t ssl_options[] = { #if defined(SSL_OP_NO_TLSv1) {"no_tlsv1", SSL_OP_NO_TLSv1}, #endif +#if defined(SSL_OP_NO_TLSv1_1) + {"no_tlsv1_1", SSL_OP_NO_TLSv1_1}, +#endif +#if defined(SSL_OP_NO_TLSv1_2) + {"no_tlsv1_2", SSL_OP_NO_TLSv1_2}, +#endif #if defined(SSL_OP_PKCS1_CHECK_1) {"pkcs1_check_1", SSL_OP_PKCS1_CHECK_1}, #endif @@ -158,6 +123,13 @@ static ssl_option_t ssl_options[] = { #endif #if defined(SSL_OP_TLS_ROLLBACK_BUG) {"tls_rollback_bug", SSL_OP_TLS_ROLLBACK_BUG}, +#endif +#if !defined(SSL_OP_NO_COMPRESSION) && (OPENSSL_VERSION_NUMBER >= 0x0090800f) && (OPENSSL_VERSION_NUMBER < 0x1000000fL) + /* Add SSL_OP_NO_COMPRESSION manually if built against 0.9.8. */ + {"no_compression", 0L}, #endif {NULL, 0L} }; + +#endif + diff --git a/src/options.lua b/src/options.lua new file mode 100644 index 0000000..1cf5ccc --- /dev/null +++ b/src/options.lua @@ -0,0 +1,98 @@ +local function usage() + print("Usage:") + print("* Generate options of your system:") + print(" lua options.lua -g /path/to/ssl.h [verion] > options.h") + print("* Examples:") + print(" lua options.lua -g /usr/include/openssl/ssl.h > options.h\n") + print(" lua options.lua -g /usr/include/openssl/ssl.h \"OpenSSL 1.0.1 14\" > options.h\n") + + print("* List options of your system:") + print(" lua options.lua -l /path/to/ssl.h\n") +end + +-- +local function printf(str, ...) + print(string.format(str, ...)) +end + +local function generate(options, version) + print([[ +#ifndef LSEC_OPTIONS_H +#define LSEC_OPTIONS_H + +/*-------------------------------------------------------------------------- + * LuaSec 0.4.1 + * Copyright (C) 2006-2011 Bruno Silvestre + * + *--------------------------------------------------------------------------*/ + +#include + +/* If you need to generate these options again, see options.lua */ +]]) + printf([[ +/* + OpenSSL version: %s +*/ +]], version) + print([[ +struct ssl_option_s { + const char *name; + unsigned long code; +}; +typedef struct ssl_option_s ssl_option_t; +]]) + + print([[static ssl_option_t ssl_options[] = {]]) + + for k, option in ipairs(options) do + local name = string.lower(string.sub(option, 8)) + print(string.format([[#if defined(%s)]], option)) + print(string.format([[ {"%s", %s},]], name, option)) + print([[#endif]]) + end + print([[ +#if !defined(SSL_OP_NO_COMPRESSION) && (OPENSSL_VERSION_NUMBER >= 0x0090800f) && (OPENSSL_VERSION_NUMBER < 0x1000000fL) + /* Add SSL_OP_NO_COMPRESSION manually if built against 0.9.8. */ + {"no_compression", 0L}, +#endif]]) + print([[ {NULL, 0L}]]) + print([[ +}; + +#endif +]]) +end + +local function loadoptions(file) + local options = {} + local f = assert(io.open(file, "r")) + for line in f:lines() do + local op = string.match(line, "define%s+(SSL_OP_%S+)") + if op then + table.insert(options, op) + end + end + table.sort(options, function(a,b) return a #include +#if defined(WIN32) +#include +#endif + #include +#include +#include #include #include #include -#include "io.h" -#include "buffer.h" -#include "timeout.h" -#include "socket.h" +#include +#include +#include +#include + +#include "x509.h" #include "ssl.h" +/** + * Underline socket error. + */ +static int lsec_socket_error() +{ +#if defined(WIN32) + return WSAGetLastError(); +#else + return errno; +#endif +} + /** * Map error code into string. */ static const char *ssl_ioerror(void *ctx, int err) { - if (err == IO_SSL) { + if (err == LSEC_IO_SSL) { p_ssl ssl = (p_ssl) ctx; switch(ssl->error) { case SSL_ERROR_NONE: return "No error"; @@ -46,11 +67,22 @@ static const char *ssl_ioerror(void *ctx, int err) */ static int meth_destroy(lua_State *L) { - p_ssl ssl = (p_ssl) lua_touserdata(L, 1); - if (ssl->ssl) { + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + if (ssl->state == LSEC_STATE_CONNECTED) { socket_setblocking(&ssl->sock); SSL_shutdown(ssl->ssl); + } + if (ssl->sock != SOCKET_INVALID) { socket_destroy(&ssl->sock); + } + ssl->state = LSEC_STATE_CLOSED; + if (ssl->ssl) { + /* Clear the registry */ + luaL_getmetatable(L, "SSL:Verify:Registry"); + lua_pushlightuserdata(L, (void*)ssl->ssl); + lua_pushnil(L); + lua_settable(L, -3); + /* Destroy the object */ SSL_free(ssl->ssl); ssl->ssl = NULL; } @@ -64,36 +96,36 @@ static int handshake(p_ssl ssl) { int err; p_timeout tm = timeout_markstart(&ssl->tm); - if (ssl->state == ST_SSL_CLOSED) + if (ssl->state == LSEC_STATE_CLOSED) return IO_CLOSED; for ( ; ; ) { ERR_clear_error(); err = SSL_do_handshake(ssl->ssl); ssl->error = SSL_get_error(ssl->ssl, err); - switch(ssl->error) { + switch (ssl->error) { case SSL_ERROR_NONE: - ssl->state = ST_SSL_CONNECTED; + ssl->state = LSEC_STATE_CONNECTED; return IO_DONE; case SSL_ERROR_WANT_READ: err = socket_waitfd(&ssl->sock, WAITFD_R, tm); - if (err == IO_TIMEOUT) return IO_SSL; + if (err == IO_TIMEOUT) return LSEC_IO_SSL; if (err != IO_DONE) return err; break; case SSL_ERROR_WANT_WRITE: err = socket_waitfd(&ssl->sock, WAITFD_W, tm); - if (err == IO_TIMEOUT) return IO_SSL; + if (err == IO_TIMEOUT) return LSEC_IO_SSL; if (err != IO_DONE) return err; break; case SSL_ERROR_SYSCALL: if (ERR_peek_error()) { ssl->error = SSL_ERROR_SSL; - return IO_SSL; + return LSEC_IO_SSL; } if (err == 0) return IO_CLOSED; - return socket_error(); + return lsec_socket_error(); default: - return IO_SSL; + return LSEC_IO_SSL; } } return IO_UNKNOWN; @@ -106,38 +138,38 @@ static int ssl_send(void *ctx, const char *data, size_t count, size_t *sent, p_timeout tm) { int err; - p_ssl ssl = (p_ssl) ctx; - if (ssl->state == ST_SSL_CLOSED) + p_ssl ssl = (p_ssl)ctx; + if (ssl->state != LSEC_STATE_CONNECTED) return IO_CLOSED; *sent = 0; for ( ; ; ) { ERR_clear_error(); - err = SSL_write(ssl->ssl, data, (int) count); + err = SSL_write(ssl->ssl, data, (int)count); ssl->error = SSL_get_error(ssl->ssl, err); - switch(ssl->error) { + switch (ssl->error) { case SSL_ERROR_NONE: *sent = err; return IO_DONE; case SSL_ERROR_WANT_READ: err = socket_waitfd(&ssl->sock, WAITFD_R, tm); - if (err == IO_TIMEOUT) return IO_SSL; + if (err == IO_TIMEOUT) return LSEC_IO_SSL; if (err != IO_DONE) return err; break; case SSL_ERROR_WANT_WRITE: err = socket_waitfd(&ssl->sock, WAITFD_W, tm); - if (err == IO_TIMEOUT) return IO_SSL; + if (err == IO_TIMEOUT) return LSEC_IO_SSL; if (err != IO_DONE) return err; break; case SSL_ERROR_SYSCALL: if (ERR_peek_error()) { ssl->error = SSL_ERROR_SSL; - return IO_SSL; + return LSEC_IO_SSL; } if (err == 0) return IO_CLOSED; - return socket_error(); + return lsec_socket_error(); default: - return IO_SSL; + return LSEC_IO_SSL; } } return IO_UNKNOWN; @@ -150,15 +182,15 @@ static int ssl_recv(void *ctx, char *data, size_t count, size_t *got, p_timeout tm) { int err; - p_ssl ssl = (p_ssl) ctx; - if (ssl->state == ST_SSL_CLOSED) + p_ssl ssl = (p_ssl)ctx; + if (ssl->state != LSEC_STATE_CONNECTED) return IO_CLOSED; *got = 0; for ( ; ; ) { ERR_clear_error(); - err = SSL_read(ssl->ssl, data, (int) count); + err = SSL_read(ssl->ssl, data, (int)count); ssl->error = SSL_get_error(ssl->ssl, err); - switch(ssl->error) { + switch (ssl->error) { case SSL_ERROR_NONE: *got = err; return IO_DONE; @@ -167,24 +199,24 @@ static int ssl_recv(void *ctx, char *data, size_t count, size_t *got, return IO_CLOSED; case SSL_ERROR_WANT_READ: err = socket_waitfd(&ssl->sock, WAITFD_R, tm); - if (err == IO_TIMEOUT) return IO_SSL; + if (err == IO_TIMEOUT) return LSEC_IO_SSL; if (err != IO_DONE) return err; break; case SSL_ERROR_WANT_WRITE: err = socket_waitfd(&ssl->sock, WAITFD_W, tm); - if (err == IO_TIMEOUT) return IO_SSL; + if (err == IO_TIMEOUT) return LSEC_IO_SSL; if (err != IO_DONE) return err; break; case SSL_ERROR_SYSCALL: if (ERR_peek_error()) { ssl->error = SSL_ERROR_SSL; - return IO_SSL; + return LSEC_IO_SSL; } if (err == 0) return IO_CLOSED; - return socket_error(); + return lsec_socket_error(); default: - return IO_SSL; + return LSEC_IO_SSL; } } return IO_UNKNOWN; @@ -196,15 +228,15 @@ static int ssl_recv(void *ctx, char *data, size_t count, size_t *got, static int meth_create(lua_State *L) { p_ssl ssl; - int mode = ctx_getmode(L, 1); - SSL_CTX *ctx = ctx_getcontext(L, 1); + int mode = lsec_getmode(L, 1); + SSL_CTX *ctx = lsec_checkcontext(L, 1); - if (mode == MD_CTX_INVALID) { + if (mode == LSEC_MODE_INVALID) { lua_pushnil(L); lua_pushstring(L, "invalid mode"); return 2; } - ssl = (p_ssl) lua_newuserdata(L, sizeof(t_ssl)); + ssl = (p_ssl)lua_newuserdata(L, sizeof(t_ssl)); if (!ssl) { lua_pushnil(L); lua_pushstring(L, "error creating SSL object"); @@ -214,21 +246,21 @@ static int meth_create(lua_State *L) if (!ssl->ssl) { lua_pushnil(L); lua_pushstring(L, "error creating SSL object"); - return 2;; + return 2; } - ssl->state = ST_SSL_NEW; - SSL_set_fd(ssl->ssl, (int) SOCKET_INVALID); + ssl->state = LSEC_STATE_NEW; + SSL_set_fd(ssl->ssl, (int)SOCKET_INVALID); SSL_set_mode(ssl->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); #if defined(SSL_MODE_RELEASE_BUFFERS) SSL_set_mode(ssl->ssl, SSL_MODE_RELEASE_BUFFERS); #endif - if (mode == MD_CTX_SERVER) + if (mode == LSEC_MODE_SERVER) SSL_set_accept_state(ssl->ssl); else SSL_set_connect_state(ssl->ssl); - io_init(&ssl->io, (p_send) ssl_send, (p_recv) ssl_recv, + io_init(&ssl->io, (p_send)ssl_send, (p_recv)ssl_recv, (p_error) ssl_ioerror, ssl); timeout_init(&ssl->tm, -1, -1); buffer_init(&ssl->buf, &ssl->io, &ssl->tm); @@ -242,7 +274,7 @@ static int meth_create(lua_State *L) * Buffer send function */ static int meth_send(lua_State *L) { - p_ssl ssl = (p_ssl) luaL_checkudata(L, 1, "SSL:Connection"); + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); return buffer_meth_send(L, &ssl->buf); } @@ -250,7 +282,7 @@ static int meth_send(lua_State *L) { * Buffer receive function */ static int meth_receive(lua_State *L) { - p_ssl ssl = (p_ssl) luaL_checkudata(L, 1, "SSL:Connection"); + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); return buffer_meth_receive(L, &ssl->buf); } @@ -259,19 +291,19 @@ static int meth_receive(lua_State *L) { */ static int meth_getfd(lua_State *L) { - p_ssl ssl = (p_ssl) luaL_checkudata(L, 1, "SSL:Connection"); + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); lua_pushnumber(L, ssl->sock); return 1; } /** * Set the TLS/SSL file descriptor. - * This is done *before* the handshake. + * Call it *before* the handshake. */ static int meth_setfd(lua_State *L) { - p_ssl ssl = (p_ssl) luaL_checkudata(L, 1, "SSL:Connection"); - if (ssl->state != ST_SSL_NEW) + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + if (ssl->state != LSEC_STATE_NEW) luaL_argerror(L, 1, "invalid SSL object state"); ssl->sock = luaL_checkint(L, 2); socket_setnonblocking(&ssl->sock); @@ -284,7 +316,7 @@ static int meth_setfd(lua_State *L) */ static int meth_handshake(lua_State *L) { - p_ssl ssl = (p_ssl) luaL_checkudata(L, 1, "SSL:Connection"); + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); int err = handshake(ssl); if (err == IO_DONE) { lua_pushboolean(L, 1); @@ -300,9 +332,7 @@ static int meth_handshake(lua_State *L) */ static int meth_close(lua_State *L) { - p_ssl ssl = (p_ssl) luaL_checkudata(L, 1, "SSL:Connection"); meth_destroy(L); - ssl->state = ST_SSL_CLOSED; return 0; } @@ -311,7 +341,7 @@ static int meth_close(lua_State *L) */ static int meth_settimeout(lua_State *L) { - p_ssl ssl = (p_ssl) luaL_checkudata(L, 1, "SSL:Connection"); + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); return timeout_meth_settimeout(L, &ssl->tm); } @@ -321,8 +351,8 @@ static int meth_settimeout(lua_State *L) static int meth_dirty(lua_State *L) { int res = 0; - p_ssl ssl = (p_ssl) luaL_checkudata(L, 1, "SSL:Connection"); - if (ssl->state != ST_SSL_CLOSED) + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + if (ssl->state != LSEC_STATE_CLOSED) res = !buffer_isempty(&ssl->buf) || SSL_pending(ssl->ssl); lua_pushboolean(L, res); return 1; @@ -333,8 +363,10 @@ static int meth_dirty(lua_State *L) */ static int meth_want(lua_State *L) { - p_ssl ssl = (p_ssl) luaL_checkudata(L, 1, "SSL:Connection"); - int code = (ssl->state == ST_SSL_CLOSED) ? SSL_NOTHING : SSL_want(ssl->ssl); + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + int code = (ssl->state == LSEC_STATE_CLOSED) + ? SSL_NOTHING + : SSL_want(ssl->ssl); switch(code) { case SSL_NOTHING: lua_pushstring(L, "nothing"); break; case SSL_READING: lua_pushstring(L, "read"); break; @@ -345,65 +377,341 @@ static int meth_want(lua_State *L) } /** - * Return a pointer to SSL structure. + * Return the compression method used. */ -static int meth_rawconn(lua_State *L) +static int meth_compression(lua_State *L) +{ + const COMP_METHOD *comp; + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + if (ssl->state != LSEC_STATE_CONNECTED) { + lua_pushnil(L); + lua_pushstring(L, "closed"); + return 2; + } + comp = SSL_get_current_compression(ssl->ssl); + if (comp) + lua_pushstring(L, SSL_COMP_get_name(comp)); + else + lua_pushnil(L); + return 1; +} + +/** + * Return the nth certificate of the peer's chain. + */ +static int meth_getpeercertificate(lua_State *L) +{ + int n; + X509 *cert; + STACK_OF(X509) *certs; + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + if (ssl->state != LSEC_STATE_CONNECTED) { + lua_pushnil(L); + lua_pushstring(L, "closed"); + return 2; + } + /* Default to the first cert */ + n = luaL_optint(L, 2, 1); + /* This function is 1-based, but OpenSSL is 0-based */ + --n; + if (n < 0) { + lua_pushnil(L); + lua_pushliteral(L, "invalid certificate index"); + return 2; + } + if (n == 0) { + cert = SSL_get_peer_certificate(ssl->ssl); + if (cert) + lsec_pushx509(L, cert); + else + lua_pushnil(L); + return 1; + } + /* In a server-context, the stack doesn't contain the peer cert, + * so adjust accordingly. + */ + if (ssl->ssl->server) + --n; + certs = SSL_get_peer_cert_chain(ssl->ssl); + if (n >= sk_X509_num(certs)) { + lua_pushnil(L); + return 1; + } + cert = sk_X509_value(certs, n); + /* Increment the reference counting of the object. */ + /* See SSL_get_peer_certificate() source code. */ + CRYPTO_add(&cert->references, 1, CRYPTO_LOCK_X509); + lsec_pushx509(L, cert); + return 1; +} + +/** + * Return the chain of certificate of the peer. + */ +static int meth_getpeerchain(lua_State *L) +{ + int i; + int idx = 1; + int n_certs; + X509 *cert; + STACK_OF(X509) *certs; + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + if (ssl->state != LSEC_STATE_CONNECTED) { + lua_pushnil(L); + lua_pushstring(L, "closed"); + return 2; + } + lua_newtable(L); + if (ssl->ssl->server) { + lsec_pushx509(L, SSL_get_peer_certificate(ssl->ssl)); + lua_rawseti(L, -2, idx++); + } + certs = SSL_get_peer_cert_chain(ssl->ssl); + n_certs = sk_X509_num(certs); + for (i = 0; i < n_certs; i++) { + cert = sk_X509_value(certs, i); + /* Increment the reference counting of the object. */ + /* See SSL_get_peer_certificate() source code. */ + CRYPTO_add(&cert->references, 1, CRYPTO_LOCK_X509); + lsec_pushx509(L, cert); + lua_rawseti(L, -2, idx++); + } + return 1; +} + +/** + * Copy the table src to the table dst. + */ +static void copy_error_table(lua_State *L, int src, int dst) +{ + lua_pushnil(L); + while (lua_next(L, src) != 0) { + if (lua_istable(L, -1)) { + /* Replace the table with its copy */ + lua_newtable(L); + copy_error_table(L, dst+2, dst+3); + lua_remove(L, dst+2); + } + lua_pushvalue(L, -2); + lua_pushvalue(L, -2); + lua_rawset(L, dst); + /* Remove the value and leave the key */ + lua_pop(L, 1); + } +} + +/** + * Return the verification state of the peer chain. + */ +static int meth_getpeerverification(lua_State *L) +{ + long err; + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + if (ssl->state != LSEC_STATE_CONNECTED) { + lua_pushboolean(L, 0); + lua_pushstring(L, "closed"); + return 2; + } + err = SSL_get_verify_result(ssl->ssl); + if (err == X509_V_OK) { + lua_pushboolean(L, 1); + return 1; + } + luaL_getmetatable(L, "SSL:Verify:Registry"); + lua_pushlightuserdata(L, (void*)ssl->ssl); + lua_gettable(L, -2); + if (lua_isnil(L, -1)) + lua_pushstring(L, X509_verify_cert_error_string(err)); + else { + /* Copy the table of errors to avoid modifications */ + lua_newtable(L); + copy_error_table(L, lua_gettop(L)-1, lua_gettop(L)); + } + lua_pushboolean(L, 0); + lua_pushvalue(L, -2); + return 2; +} + +/** + * Get the latest "Finished" message sent out. + */ +static int meth_getfinished(lua_State *L) +{ + size_t len = 0; + char *buffer = NULL; + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + if (ssl->state != LSEC_STATE_CONNECTED) { + lua_pushnil(L); + lua_pushstring(L, "closed"); + return 2; + } + if ((len = SSL_get_finished(ssl->ssl, NULL, 0)) == 0) + return 0; + buffer = (char*)malloc(len); + if (!buffer) { + lua_pushnil(L); + lua_pushstring(L, "out of memory"); + return 2; + } + SSL_get_finished(ssl->ssl, buffer, len); + lua_pushlstring(L, buffer, len); + free(buffer); + return 1; +} + +/** + * Gets the latest "Finished" message received. + */ +static int meth_getpeerfinished(lua_State *L) +{ + size_t len = 0; + char *buffer = NULL; + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + if (ssl->state != LSEC_STATE_CONNECTED) { + lua_pushnil(L); + lua_pushstring(L, "closed"); + return 0; + } + if ((len = SSL_get_peer_finished(ssl->ssl, NULL, 0)) == 0) + return 0; + buffer = (char*)malloc(len); + if (!buffer) { + lua_pushnil(L); + lua_pushstring(L, "out of memory"); + return 2; + } + SSL_get_peer_finished(ssl->ssl, buffer, len); + lua_pushlstring(L, buffer, len); + free(buffer); + return 1; +} + +/** + * Object information -- tostring metamethod + */ +static int meth_tostring(lua_State *L) { p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); - lua_pushlightuserdata(L, (void*)ssl->ssl); + lua_pushfstring(L, "SSL connection: %p%s", ssl, + ssl->state == LSEC_STATE_CLOSED ? " (closed)" : ""); + return 1; +} + +/** + * Add a method in the SSL metatable. + */ +static int meth_setmethod(lua_State *L) +{ + luaL_getmetatable(L, "SSL:Connection"); + lua_pushstring(L, "__index"); + lua_gettable(L, -2); + lua_pushvalue(L, 1); + lua_pushvalue(L, 2); + lua_settable(L, -3); + return 0; +} + +/** + * Return information about the connection. + */ +static int meth_info(lua_State *L) +{ + int bits = 0; + int algbits = 0; + char buf[256] = {0}; + const SSL_CIPHER *cipher; + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + cipher = SSL_get_current_cipher(ssl->ssl); + if (!cipher) + return 0; + SSL_CIPHER_description(cipher, buf, sizeof(buf)); + bits = SSL_CIPHER_get_bits(cipher, &algbits); + lua_pushstring(L, buf); + lua_pushnumber(L, bits); + lua_pushnumber(L, algbits); + return 3; +} + +static int meth_copyright(lua_State *L) +{ + lua_pushstring(L, "LuaSec 0.4.1 - Copyright (C) 2006-2011 Bruno Silvestre" +#if defined(WITH_LUASOCKET) + "\nLuaSocket 2.0.2 - Copyright (C) 2004-2007 Diego Nehab" +#endif + ); return 1; } /*---------------------------------------------------------------------------*/ +/** + * SSL methods + */ +static luaL_Reg methods[] = { + {"close", meth_close}, + {"getfd", meth_getfd}, + {"getfinished", meth_getfinished}, + {"getpeercertificate", meth_getpeercertificate}, + {"getpeerchain", meth_getpeerchain}, + {"getpeerverification", meth_getpeerverification}, + {"getpeerfinished", meth_getpeerfinished}, + {"dirty", meth_dirty}, + {"dohandshake", meth_handshake}, + {"receive", meth_receive}, + {"send", meth_send}, + {"settimeout", meth_settimeout}, + {"want", meth_want}, + {NULL, NULL} +}; /** - * SSL metamethods + * SSL metamethods. */ static luaL_Reg meta[] = { - {"close", meth_close}, - {"getfd", meth_getfd}, - {"dirty", meth_dirty}, - {"dohandshake", meth_handshake}, - {"receive", meth_receive}, - {"send", meth_send}, - {"settimeout", meth_settimeout}, - {"want", meth_want}, + {"__gc", meth_destroy}, + {"__tostring", meth_tostring}, + {NULL, NULL} +}; + +/** + * SSL functions. + */ +static luaL_Reg funcs[] = { + {"compression", meth_compression}, + {"create", meth_create}, + {"info", meth_info}, + {"setfd", meth_setfd}, + {"setmethod", meth_setmethod}, + {"copyright", meth_copyright}, {NULL, NULL} }; /** - * SSL functions + * Initialize modules. */ -static luaL_Reg funcs[] = { - {"create", meth_create}, - {"setfd", meth_setfd}, - {"rawconnection", meth_rawconn}, - {NULL, NULL} -}; - -/** - * Initialize modules - */ -LUASEC_API int luaopen_ssl_core(lua_State *L) +#if (LUA_VERSION_NUM == 501) +LSEC_API int luaopen_ssl_core(lua_State *L) { /* Initialize SSL */ if (!SSL_library_init()) { lua_pushstring(L, "unable to initialize SSL library"); lua_error(L); } + OpenSSL_add_all_algorithms(); SSL_load_error_strings(); +#if defined(WITH_LUASOCKET) /* Initialize internal library */ socket_open(); +#endif - /* Registre the functions and tables */ + /* Register the functions and tables */ luaL_newmetatable(L, "SSL:Connection"); - lua_newtable(L); luaL_register(L, NULL, meta); + + lua_newtable(L); + luaL_register(L, NULL, methods); lua_setfield(L, -2, "__index"); - lua_pushcfunction(L, meth_destroy); - lua_setfield(L, -2, "__gc"); luaL_register(L, "ssl.core", funcs); lua_pushnumber(L, SOCKET_INVALID); @@ -411,4 +719,35 @@ LUASEC_API int luaopen_ssl_core(lua_State *L) return 1; } +#else +LSEC_API int luaopen_ssl_core(lua_State *L) +{ + /* Initialize SSL */ + if (!SSL_library_init()) { + lua_pushstring(L, "unable to initialize SSL library"); + lua_error(L); + } + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); +#if defined(WITH_LUASOCKET) + /* Initialize internal library */ + socket_open(); +#endif + + /* Register the functions and tables */ + luaL_newmetatable(L, "SSL:Connection"); + luaL_setfuncs(L, meta, 0); + + lua_newtable(L); + luaL_setfuncs(L, methods, 0); + lua_setfield(L, -2, "__index"); + + lua_newtable(L); + luaL_setfuncs(L, funcs, 0); + lua_pushnumber(L, SOCKET_INVALID); + lua_setfield(L, -2, "invalidfd"); + + return 1; +} +#endif diff --git a/src/ssl.h b/src/ssl.h index a3e4d25..5d381ce 100644 --- a/src/ssl.h +++ b/src/ssl.h @@ -1,5 +1,5 @@ -#ifndef __SSL_H__ -#define __SSL_H__ +#ifndef LSEC_SSL_H +#define LSEC_SSL_H /*-------------------------------------------------------------------------- * LuaSec 0.4.1 @@ -10,14 +10,19 @@ #include #include -#include "io.h" -#include "buffer.h" -#include "timeout.h" +#include +#include +#include +#include + +#include "config.h" #include "context.h" -#define ST_SSL_NEW 1 -#define ST_SSL_CONNECTED 2 -#define ST_SSL_CLOSED 3 +#define LSEC_STATE_NEW 1 +#define LSEC_STATE_CONNECTED 2 +#define LSEC_STATE_CLOSED 3 + +#define LSEC_IO_SSL -100 typedef struct t_ssl_ { t_socket sock; @@ -25,11 +30,11 @@ typedef struct t_ssl_ { t_buffer buf; t_timeout tm; SSL *ssl; - char state; + int state; int error; } t_ssl; typedef t_ssl* p_ssl; -LUASEC_API int luaopen_ssl_core(lua_State *L); +LSEC_API int luaopen_ssl_core(lua_State *L); #endif diff --git a/src/ssl.lua b/src/ssl.lua index 0170bc8..1b062f6 100644 --- a/src/ssl.lua +++ b/src/ssl.lua @@ -4,19 +4,21 @@ -- ------------------------------------------------------------------------------ +local core = require("ssl.core") +local context = require("ssl.context") +local x509 = require("ssl.x509") + module("ssl", package.seeall) -require("ssl.core") -require("ssl.context") +_VERSION = "0.5.PR" +_COPYRIGHT = core.copyright() +-- Export +loadcertificate = x509.load -_VERSION = "0.4.1" -_COPYRIGHT = "LuaSec 0.4.1 - Copyright (C) 2006-2011 Bruno Silvestre\n" .. - "LuaSocket 2.0.2 - Copyright (C) 2004-2007 Diego Nehab" - --- Export functions -rawconnection = core.rawconnection -rawcontext = context.rawcontext +-- We must prevent the contexts to be collected before the connections, +-- otherwise the C registry will be cleared. +local registry = setmetatable({}, {__mode="k"}) -- -- @@ -45,6 +47,12 @@ function newcontext(cfg) if not succ then return nil, msg end -- Load the key if cfg.key then + if cfg.password and + type(cfg.password) ~= "function" and + type(cfg.password) ~= "string" + then + return nil, "invalid password type" + end succ, msg = context.loadkey(ctx, cfg.key, cfg.password) if not succ then return nil, msg end end @@ -58,6 +66,11 @@ function newcontext(cfg) succ, msg = context.locations(ctx, cfg.cafile, cfg.capath) if not succ then return nil, msg end end + -- Set SSL ciphers + if cfg.ciphers then + succ, msg = context.setcipher(ctx, cfg.ciphers) + if not succ then return nil, msg end + end -- Set the verification options succ, msg = optexec(context.setverify, cfg.verify, ctx) if not succ then return nil, msg end @@ -69,6 +82,24 @@ function newcontext(cfg) succ, msg = context.setdepth(ctx, cfg.depth) if not succ then return nil, msg end end + -- Set DH parameters + if cfg.dhparam then + if type(cfg.dhparam) ~= "function" then + return nil, "invalid DH parameter type" + end + context.setdhparam(ctx, cfg.dhparam) + end + -- Set elliptic curve + if cfg.curve then + succ, msg = context.setcurve(ctx, cfg.curve) + if not succ then return nil, msg end + end + -- Set extra verification options + if cfg.verifyext and ctx.setverifyext then + succ, msg = optexec(ctx.setverifyext, cfg.verifyext, ctx) + if not succ then return nil, msg end + end + return ctx end @@ -87,7 +118,43 @@ function wrap(sock, cfg) if s then core.setfd(s, sock:getfd()) sock:setfd(core.invalidfd) + registry[s] = ctx return s end return nil, msg end + +-- +-- Extract connection information. +-- +local function info(ssl, field) + local str, comp, err + comp, err = core.compression(ssl) + if err then + return comp, err + end + -- Avoid parser + if field == "compression" then + return comp + end + local info = {compression = comp} + str, info.bits, info.algbits = core.info(ssl) + if str then + info.cipher, info.protocol, info.key, + info.authentication, info.encryption, info.mac = + string.match(str, + "^(%S+)%s+(%S+)%s+Kx=(%S+)%s+Au=(%S+)%s+Enc=(%S+)%s+Mac=(%S+)") + info.export = (string.match(str, "%sexport%s*$") ~= nil) + end + if field then + return info[field] + end + -- Empty? + return ( (next(info)) and info ) +end + +-- +-- Set method for SSL connections. +-- +core.setmethod("info", info) + diff --git a/src/x509.c b/src/x509.c new file mode 100644 index 0000000..104cfd3 --- /dev/null +++ b/src/x509.c @@ -0,0 +1,477 @@ +/*-------------------------------------------------------------------------- + * LuaSec 0.4.1 + * Copyright (C) 2012 + * + *--------------------------------------------------------------------------*/ + +#include + +#if defined(WIN32) +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "x509.h" + +static const char* hex_tab = "0123456789abcdef"; + +/** + * Push the certificate on the stack. + */ +void lsec_pushx509(lua_State* L, X509 *cert) +{ + p_x509 cert_obj = (p_x509)lua_newuserdata(L, sizeof(t_x509)); + cert_obj->cert = cert; + luaL_getmetatable(L, "SSL:Certificate"); + lua_setmetatable(L, -2); +} + +/** + * Return the OpenSSL certificate X509. + */ +X509* lsec_checkx509(lua_State* L, int idx) +{ + return ((p_x509)luaL_checkudata(L, idx, "SSL:Certificate"))->cert; +} + +/*---------------------------------------------------------------------------*/ + +/** + * Convert the buffer 'in' to hexadecimal. + */ +static void to_hex(const char* in, int length, char* out) +{ + int i; + for (i = 0; i < length; i++) { + out[i*2] = hex_tab[(in[i] >> 4) & 0xF]; + out[i*2+1] = hex_tab[(in[i]) & 0xF]; + } +} + +/** + * Converts the ASN1_OBJECT into a textual representation and put it + * on the Lua stack. + */ +static void push_asn1_objname(lua_State* L, ASN1_OBJECT *object, int no_name) +{ + char buffer[256]; + int len = OBJ_obj2txt(buffer, sizeof(buffer), object, no_name); + len = (len < sizeof(buffer)) ? len : sizeof(buffer); + lua_pushlstring(L, buffer, len); +} + +/** + * Push the ASN1 string on the stack. + */ +static void push_asn1_string(lua_State* L, ASN1_STRING *string) +{ + if (string) + lua_pushlstring(L, (char*)ASN1_STRING_data(string), + ASN1_STRING_length(string)); + else + lua_pushnil(L); +} + +/** + * Return a human readable time. + */ +static int push_asn1_time(lua_State *L, ASN1_UTCTIME *tm) +{ + char *tmp; + long size; + BIO *out = BIO_new(BIO_s_mem()); + ASN1_TIME_print(out, tm); + size = BIO_get_mem_data(out, &tmp); + lua_pushlstring(L, tmp, size); + BIO_free(out); + return 1; +} + +/** + * + */ +static int push_subtable(lua_State* L, int idx) +{ + lua_pushvalue(L, -1); + lua_gettable(L, idx-1); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + lua_newtable(L); + lua_pushvalue(L, -2); + lua_pushvalue(L, -2); + lua_settable(L, idx-3); + lua_replace(L, -2); /* Replace key with table */ + return 1; + } + lua_replace(L, -2); /* Replace key with table */ + return 0; +} + +/** + * Retrive the general names from the object. + */ +static int push_x509_name(lua_State* L, X509_NAME *name) +{ + int i; + int n_entries; + ASN1_OBJECT *object; + X509_NAME_ENTRY *entry; + lua_newtable(L); + n_entries = X509_NAME_entry_count(name); + for (i = 0; i < n_entries; i++) { + entry = X509_NAME_get_entry(name, i); + object = X509_NAME_ENTRY_get_object(entry); + lua_newtable(L); + push_asn1_objname(L, object, 1); + lua_setfield(L, -2, "oid"); + push_asn1_objname(L, object, 0); + lua_setfield(L, -2, "name"); + push_asn1_string(L, X509_NAME_ENTRY_get_data(entry)); + lua_setfield(L, -2, "value"); + lua_rawseti(L, -2, i+1); + } + return 1; +} + +/*---------------------------------------------------------------------------*/ + +/** + * Retrive the Subject from the certificate. + */ +static int meth_subject(lua_State* L) +{ + return push_x509_name(L, X509_get_subject_name(lsec_checkx509(L, 1))); +} + +/** + * Retrive the Issuer from the certificate. + */ +static int meth_issuer(lua_State* L) +{ + return push_x509_name(L, X509_get_issuer_name(lsec_checkx509(L, 1))); +} + +/** + * Retrieve the extensions from the certificate. + */ +int meth_extensions(lua_State* L) +{ + int j; + int i = -1; + int n_general_names; + OTHERNAME *otherName; + X509_EXTENSION *extension; + GENERAL_NAME *general_name; + STACK_OF(GENERAL_NAME) *values; + X509 *peer = lsec_checkx509(L, 1); + + /* Return (ret) */ + lua_newtable(L); + + while ((i = X509_get_ext_by_NID(peer, NID_subject_alt_name, i)) != -1) { + extension = X509_get_ext(peer, i); + if (extension == NULL) + break; + values = X509V3_EXT_d2i(extension); + if (values == NULL) + break; + + /* Push ret[oid] */ + push_asn1_objname(L, extension->object, 1); + push_subtable(L, -2); + + /* Set ret[oid].name = name */ + push_asn1_objname(L, extension->object, 0); + lua_setfield(L, -2, "name"); + + n_general_names = sk_GENERAL_NAME_num(values); + for (j = 0; j < n_general_names; j++) { + general_name = sk_GENERAL_NAME_value(values, j); + switch (general_name->type) { + case GEN_OTHERNAME: + otherName = general_name->d.otherName; + push_asn1_objname(L, otherName->type_id, 1); + if (push_subtable(L, -2)) { + push_asn1_objname(L, otherName->type_id, 0); + lua_setfield(L, -2, "name"); + } + push_asn1_string(L, otherName->value->value.asn1_string); + lua_rawseti(L, -2, lua_rawlen(L, -2) + 1); + lua_pop(L, 1); + break; + case GEN_DNS: + lua_pushstring(L, "dNSName"); + push_subtable(L, -2); + push_asn1_string(L, general_name->d.dNSName); + lua_rawseti(L, -2, lua_rawlen(L, -2) + 1); + lua_pop(L, 1); + break; + case GEN_EMAIL: + lua_pushstring(L, "rfc822Name"); + push_subtable(L, -2); + push_asn1_string(L, general_name->d.rfc822Name); + lua_rawseti(L, -2, lua_rawlen(L, -2) + 1); + lua_pop(L, 1); + break; + case GEN_URI: + lua_pushstring(L, "uniformResourceIdentifier"); + push_subtable(L, -2); + push_asn1_string(L, general_name->d.uniformResourceIdentifier); + lua_rawseti(L, -2, lua_rawlen(L, -2)+1); + lua_pop(L, 1); + break; + case GEN_IPADD: + lua_pushstring(L, "iPAddress"); + push_subtable(L, -2); + push_asn1_string(L, general_name->d.iPAddress); + lua_rawseti(L, -2, lua_rawlen(L, -2)+1); + lua_pop(L, 1); + break; + case GEN_X400: + /* x400Address */ + /* not supported */ + break; + case GEN_DIRNAME: + /* directoryName */ + /* not supported */ + break; + case GEN_EDIPARTY: + /* ediPartyName */ + /* not supported */ + break; + case GEN_RID: + /* registeredID */ + /* not supported */ + break; + } + } + lua_pop(L, 1); /* ret[oid] */ + i++; /* Next extension */ + } + return 1; +} + +/** + * Convert the certificate to PEM format. + */ +static int meth_pem(lua_State* L) +{ + char* data; + long bytes; + X509* cert = lsec_checkx509(L, 1); + BIO *bio = BIO_new(BIO_s_mem()); + if (!PEM_write_bio_X509(bio, cert)) { + lua_pushnil(L); + return 1; + } + bytes = BIO_get_mem_data(bio, &data); + if (bytes > 0) + lua_pushlstring(L, data, bytes); + else + lua_pushnil(L); + BIO_free(bio); + return 1; +} + +/** + * Compute the fingerprint. + */ +static int meth_digest(lua_State* L) +{ + unsigned int bytes; + const EVP_MD *digest = NULL; + unsigned char buffer[EVP_MAX_MD_SIZE]; + char hex_buffer[EVP_MAX_MD_SIZE*2]; + X509 *cert = lsec_checkx509(L, 1); + const char *str = luaL_optstring(L, 2, NULL); + if (!str) + digest = EVP_sha1(); + else { + if (!strcmp(str, "sha1")) + digest = EVP_sha1(); + else if (!strcmp(str, "sha256")) + digest = EVP_sha256(); + else if (!strcmp(str, "sha512")) + digest = EVP_sha512(); + } + if (!digest) { + lua_pushnil(L); + lua_pushstring(L, "digest algorithm not supported"); + return 2; + } + if (!X509_digest(cert, digest, buffer, &bytes)) { + lua_pushnil(L); + lua_pushstring(L, "error processing the certificate"); + return 2; + } + to_hex((char*)buffer, bytes, hex_buffer); + lua_pushlstring(L, hex_buffer, bytes*2); + return 1; +} + +/** + * Check if the certificate is valid in a given time. + */ +static int meth_valid_at(lua_State* L) +{ + X509* cert = lsec_checkx509(L, 1); + time_t time = luaL_checkinteger(L, 2); + lua_pushboolean(L, (X509_cmp_time(X509_get_notAfter(cert), &time) >= 0 + && X509_cmp_time(X509_get_notBefore(cert), &time) <= 0)); + return 1; +} + +/** + * Return the serial number. + */ +static int meth_serial(lua_State *L) +{ + char *tmp; + BIGNUM *bn; + ASN1_INTEGER *serial; + X509* cert = lsec_checkx509(L, 1); + serial = X509_get_serialNumber(cert); + bn = ASN1_INTEGER_to_BN(serial, NULL); + tmp = BN_bn2hex(bn); + lua_pushstring(L, tmp); + BN_free(bn); + OPENSSL_free(tmp); + return 1; +} + +/** + * Return not before date. + */ +static int meth_notbefore(lua_State *L) +{ + X509* cert = lsec_checkx509(L, 1); + return push_asn1_time(L, X509_get_notBefore(cert)); +} + +/** + * Return not after date. + */ +static int meth_notafter(lua_State *L) +{ + X509* cert = lsec_checkx509(L, 1); + return push_asn1_time(L, X509_get_notAfter(cert)); +} + +/** + * Collect X509 objects. + */ +static int meth_destroy(lua_State* L) +{ + X509_free(lsec_checkx509(L, 1)); + return 0; +} + +static int meth_tostring(lua_State *L) +{ + X509* cert = lsec_checkx509(L, 1); + lua_pushfstring(L, "X509 certificate: %p", cert); + return 1; +} + +/*---------------------------------------------------------------------------*/ + +static int load_cert(lua_State* L) +{ + X509 *cert; + size_t bytes; + const char* data; + BIO *bio = BIO_new(BIO_s_mem()); + data = luaL_checklstring(L, 1, &bytes); + BIO_write(bio, data, bytes); + cert = PEM_read_bio_X509(bio, NULL, NULL, NULL); + if (cert) + lsec_pushx509(L, cert); + else + lua_pushnil(L); + BIO_free(bio); + return 1; +} + +/*---------------------------------------------------------------------------*/ + +/** + * Certificate methods. + */ +static luaL_Reg methods[] = { + {"digest", meth_digest}, + {"extensions", meth_extensions}, + {"issuer", meth_issuer}, + {"notbefore", meth_notbefore}, + {"notafter", meth_notafter}, + {"pem", meth_pem}, + {"serial", meth_serial}, + {"subject", meth_subject}, + {"validat", meth_valid_at}, + {NULL, NULL} +}; + +/** + * X509 metamethods. + */ +static luaL_Reg meta[] = { + {"__gc", meth_destroy}, + {"__tostring", meth_tostring}, + {NULL, NULL} +}; + +/** + * X509 functions. + */ +static luaL_Reg funcs[] = { + {"load", load_cert}, + {NULL, NULL} +}; + +/*--------------------------------------------------------------------------*/ + +#if (LUA_VERSION_NUM == 501) + +LSEC_API int luaopen_ssl_x509(lua_State *L) +{ + /* Register the functions and tables */ + luaL_newmetatable(L, "SSL:Certificate"); + luaL_register(L, NULL, meta); + + lua_newtable(L); + luaL_register(L, NULL, methods); + lua_setfield(L, -2, "__index"); + + luaL_register(L, "ssl.x509", funcs); + + return 1; +} + +#else + +LSEC_API int luaopen_ssl_x509(lua_State *L) +{ + /* Register the functions and tables */ + luaL_newmetatable(L, "SSL:Certificate"); + luaL_setfuncs(L, meta, 0); + + lua_newtable(L); + luaL_setfuncs(L, methods, 0); + lua_setfield(L, -2, "__index"); + + lua_newtable(L); + luaL_setfuncs(L, funcs, 0); + + return 1; +} + +#endif diff --git a/src/x509.h b/src/x509.h new file mode 100644 index 0000000..ffe212e --- /dev/null +++ b/src/x509.h @@ -0,0 +1,25 @@ +#ifndef LSEC_X509_H +#define LSEC_X509_H + +/*-------------------------------------------------------------------------- + * LuaSec 0.4.1 + * Copyright (C) 2012 + * + *--------------------------------------------------------------------------*/ + +#include +#include + +#include "config.h" + +typedef struct t_x509_ { + X509 *cert; +} t_x509; +typedef t_x509* p_x509; + +void lsec_pushx509(lua_State* L, X509* cert); +X509* lsec_checkx509(lua_State* L, int idx); + +LSEC_API int luaopen_ssl_x509(lua_State *L); + +#endif