File: //opt/imunify360-webshield/lualib/resty/openssl/ssl.lua
local ffi = require "ffi"
local C = ffi.C
local ffi_str = ffi.string
local ffi_cast = ffi.cast
require "resty.openssl.include.ssl"
local nginx_aux = require("resty.openssl.auxiliary.nginx")
local x509_lib = require("resty.openssl.x509")
local chain_lib = require("resty.openssl.x509.chain")
local stack_lib = require("resty.openssl.stack")
local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
local format_error = require("resty.openssl.err").format_error
local _M = {
SSL_VERIFY_NONE = 0x00,
SSL_VERIFY_PEER = 0x01,
SSL_VERIFY_FAIL_IF_NO_PEER_CERT = 0x02,
SSL_VERIFY_CLIENT_ONCE = 0x04,
SSL_VERIFY_POST_HANDSHAKE = 0x08,
}
local ops = {
SSL_OP_NO_EXTENDED_MASTER_SECRET = 0x00000001,
SSL_OP_CLEANSE_PLAINTEXT = 0x00000002,
SSL_OP_LEGACY_SERVER_CONNECT = 0x00000004,
SSL_OP_TLSEXT_PADDING = 0x00000010,
SSL_OP_SAFARI_ECDHE_ECDSA_BUG = 0x00000040,
SSL_OP_IGNORE_UNEXPECTED_EOF = 0x00000080,
SSL_OP_DISABLE_TLSEXT_CA_NAMES = 0x00000200,
SSL_OP_ALLOW_NO_DHE_KEX = 0x00000400,
SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS = 0x00000800,
SSL_OP_NO_QUERY_MTU = 0x00001000,
SSL_OP_COOKIE_EXCHANGE = 0x00002000,
SSL_OP_NO_TICKET = 0x00004000,
SSL_OP_CISCO_ANYCONNECT = 0x00008000,
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION = 0x00010000,
SSL_OP_NO_COMPRESSION = 0x00020000,
SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION = 0x00040000,
SSL_OP_NO_ENCRYPT_THEN_MAC = 0x00080000,
SSL_OP_ENABLE_MIDDLEBOX_COMPAT = 0x00100000,
SSL_OP_PRIORITIZE_CHACHA = 0x00200000,
SSL_OP_CIPHER_SERVER_PREFERENCE = 0x00400000,
SSL_OP_TLS_ROLLBACK_BUG = 0x00800000,
SSL_OP_NO_ANTI_REPLAY = 0x01000000,
SSL_OP_NO_SSLv3 = 0x02000000,
SSL_OP_NO_TLSv1 = 0x04000000,
SSL_OP_NO_TLSv1_2 = 0x08000000,
SSL_OP_NO_TLSv1_1 = 0x10000000,
SSL_OP_NO_TLSv1_3 = 0x20000000,
SSL_OP_NO_DTLSv1 = 0x04000000,
SSL_OP_NO_DTLSv1_2 = 0x08000000,
SSL_OP_NO_RENEGOTIATION = 0x40000000,
SSL_OP_CRYPTOPRO_TLSEXT_BUG = 0x80000000,
}
ops.SSL_OP_NO_SSL_MASK = ops.SSL_OP_NO_SSLv3 + ops.SSL_OP_NO_TLSv1 + ops.SSL_OP_NO_TLSv1_1
+ ops.SSL_OP_NO_TLSv1_2 + ops.SSL_OP_NO_TLSv1_3
ops.SSL_OP_NO_DTLS_MASK = ops.SSL_OP_NO_DTLSv1 + ops.SSL_OP_NO_DTLSv1_2
for k, v in pairs(ops) do
_M[k] = v
end
local mt = {__index = _M}
local ssl_ptr_ct = ffi.typeof('SSL*')
local stack_of_ssl_cipher_iter = function(ctx)
return stack_lib.mt_of("SSL_CIPHER", function(x) return x end, {}, true).__ipairs({ctx = ctx})
end
function _M.from_request()
-- don't GC this
local ctx, err = nginx_aux.get_req_ssl()
if err ~= nil then
return nil, err
end
return setmetatable({
ctx = ctx,
-- the cdata is not manage by Lua, don't GC on Lua side
_managed = false,
-- this is the client SSL session
_server = true,
}, mt)
end
function _M.from_socket(socket)
if not socket then
return nil, "expect a ngx.socket.tcp instance at #1"
end
-- don't GC this
local ctx, err = nginx_aux.get_socket_ssl(socket)
if err ~= nil then
return nil, err
end
return setmetatable({
ctx = ctx,
-- the cdata is not manage by Lua, don't GC on Lua side
_managed = false,
-- this is the client SSL session
_server = false,
}, mt)
end
function _M.istype(l)
return l and l.ctx and ffi.istype(ssl_ptr_ct, l.ctx)
end
function _M:get_peer_certificate()
local x509
if OPENSSL_3X then
x509 = C.SSL_get1_peer_certificate(self.ctx)
else
x509 = C.SSL_get_peer_certificate(self.ctx)
end
if x509 == nil then
return nil
end
ffi.gc(x509, C.X509_free)
local err
-- always copy, although the ref counter of returned x509 is
-- already increased by one.
x509, err = x509_lib.dup(x509)
if err then
return nil, err
end
return x509
end
function _M:get_peer_cert_chain()
local stack = C.SSL_get_peer_cert_chain(self.ctx)
if stack == nil then
return nil
end
return chain_lib.dup(stack)
end
-- TLSv1.3
function _M:set_ciphersuites(ciphers)
if C.SSL_set_ciphersuites(self.ctx, ciphers) ~= 1 then
return false, format_error("ssl:set_ciphers: SSL_set_ciphersuites")
end
return true
end
-- TLSv1.2 and lower
function _M:set_cipher_list(ciphers)
if C.SSL_set_cipher_list(self.ctx, ciphers) ~= 1 then
return false, format_error("ssl:set_ciphers: SSL_set_cipher_list")
end
return true
end
function _M:get_ciphers()
local ciphers = C.SSL_get_ciphers(self.ctx)
if ciphers == nil then
return nil
end
local ret = {}
for i, cipher in stack_of_ssl_cipher_iter(ciphers) do
cipher = C.SSL_CIPHER_get_name(cipher)
if cipher == nil then
return nil, format_error("ssl:get_ciphers: SSL_CIPHER_get_name")
end
ret[i] = ffi_str(cipher)
end
return table.concat(ret, ":")
end
function _M:get_cipher_name()
local cipher = C.SSL_get_current_cipher(self.ctx)
if cipher == nil then
return nil
end
cipher = C.SSL_CIPHER_get_name(cipher)
if cipher == nil then
return nil, format_error("ssl:get_cipher_name: SSL_CIPHER_get_name")
end
return ffi_str(cipher)
end
function _M:set_timeout(tm)
local session = C.SSL_get_session(self.ctx)
if session == nil then
return false, format_error("ssl:set_timeout: SSL_get_session")
end
if C.SSL_SESSION_set_timeout(session, tm) ~= 1 then
return false, format_error("ssl:set_timeout: SSL_SESSION_set_timeout")
end
return true
end
function _M:get_timeout()
local session = C.SSL_get_session(self.ctx)
if session == nil then
return false, format_error("ssl:get_timeout: SSL_get_session")
end
return tonumber(C.SSL_SESSION_get_timeout(session))
end
local ssl_verify_default_cb = ffi_cast("verify_callback", function()
return 1
end)
function _M:set_verify(mode, cb)
if self._verify_cb then
self._verify_cb:free()
end
if cb then
cb = ffi_cast("verify_callback", cb)
self._verify_cb = cb
end
C.SSL_set_verify(self.ctx, mode, cb or ssl_verify_default_cb)
return true
end
function _M:free_verify_cb()
if self._verify_cb then
self._verify_cb:free()
self._verify_cb = nil
end
end
function _M:add_client_ca(x509)
if not self._server then
return false, "ssl:add_client_ca is only supported on server side"
end
if not x509_lib.istype(x509) then
return false, "expect a x509 instance at #1"
end
if C.SSL_add_client_CA(self.ctx, x509.ctx) ~= 1 then
return false, format_error("ssl:add_client_ca: SSL_add_client_CA")
end
return true
end
function _M:set_options(...)
local bitmask = 0
for _, opt in ipairs({...}) do
bitmask = bit.bor(bitmask, opt)
end
bitmask = C.SSL_set_options(self.ctx, bitmask)
return tonumber(bitmask)
end
function _M:get_options(readable)
local bitmask = C.SSL_get_options(self.ctx)
if not readable then
return tonumber(bitmask)
end
local ret = {}
for k, v in pairs(ops) do
if bit.band(v, bitmask) > 0 then
table.insert(ret, k)
end
end
table.sort(ret)
return ret
end
function _M:clear_options(...)
local bitmask = 0
for _, opt in ipairs({...}) do
bitmask = bit.bor(bitmask, opt)
end
bitmask = C.SSL_clear_options(self.ctx, bitmask)
return tonumber(bitmask)
end
local valid_protocols = {
["SSLv3"] = ops.SSL_OP_NO_SSLv3,
["TLSv1"] = ops.SSL_OP_NO_TLSv1,
["TLSv1.1"] = ops.SSL_OP_NO_TLSv1_1,
["TLSv1.2"] = ops.SSL_OP_NO_TLSv1_2,
["TLSv1.3"] = ops.SSL_OP_NO_TLSv1_3,
}
local any_tlsv1 = ops.SSL_OP_NO_TLSv1_1 + ops.SSL_OP_NO_TLSv1_2 + ops.SSL_OP_NO_TLSv1_3
function _M:set_protocols(...)
local bitmask = 0
for _, prot in ipairs({...}) do
local b = valid_protocols[prot]
if not b then
return nil, "\"" .. prot .. "\" is not a valid protocol"
end
bitmask = bit.bor(bitmask, b)
end
if bit.band(bitmask, any_tlsv1) > 0 then
bitmask = bit.bor(bitmask, ops.SSL_OP_NO_TLSv1)
end
-- first disable all protocols
C.SSL_set_options(self.ctx, ops.SSL_OP_NO_SSL_MASK)
-- then enable selected protocols
return tonumber(C.SSL_clear_options(self.ctx, bitmask))
end
return _M