mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
529f0c5ad release: 1.5.0 1f7d7b326 tests(*) unload provider to make valgrind happy 766955521 fix(param) fix issue when gettable schema may be overwritten by settable schema 8c366c22c fix(param) save converted value to prevent potential use-after-free a0711de99 fix(x509.csr) fix potential use-after-free in set_extension and add_extension 407d31ec3 fix(x509.*) fix potential use-after-free when get or set subject_alt_name, info_access and dist_points e0872dcfa chore(x509.*) use const type name b16f759c2 fix(x509.store) fix potential use-after-free in store:verify and store:check_revocation 48ab40148 tests(ci) catch more GC corner cases e924ee045 fix(pkey) fix potential use-after-free in pkey.paramgen (#176) 224fae68c fix(bn) fix potential use-after-free in bn.new (#177) a88f1ba30 fix(x509.store) fix the string is not NUL terminated in set_purpose (#174) d94064cc7 fix(objects): fix a buffer overflow issue in find_sigid_algs. (#175) 7d6d8b5d2 fix(asn1) correct time_t to be 64 bits type (#171) 30bc5b7f4 doc(examples) update comment for raw-sign-and-recover git-subtree-dir: src/deps/src/lua-resty-openssl git-subtree-split: 529f0c5ad1a3275a2313b68003650e7c5693dc3d
451 lines
12 KiB
Lua
451 lines
12 KiB
Lua
local ffi = require("ffi")
|
|
local C = ffi.C
|
|
local ffi_cast = ffi.cast
|
|
local ffi_str = ffi.string
|
|
|
|
local format_error = require("resty.openssl.err").format_error
|
|
|
|
local OPENSSL_3X
|
|
|
|
local function try_require_modules()
|
|
package.loaded["resty.openssl.version"] = nil
|
|
|
|
local pok, lib = pcall(require, "resty.openssl.version")
|
|
if pok then
|
|
OPENSSL_3X = lib.OPENSSL_3X
|
|
|
|
require "resty.openssl.include.crypto"
|
|
require "resty.openssl.include.objects"
|
|
else
|
|
package.loaded["resty.openssl.version"] = nil
|
|
end
|
|
end
|
|
try_require_modules()
|
|
|
|
|
|
local _M = {
|
|
_VERSION = '1.5.0',
|
|
}
|
|
|
|
function _M.load_modules()
|
|
_M.bn = require("resty.openssl.bn")
|
|
_M.cipher = require("resty.openssl.cipher")
|
|
_M.digest = require("resty.openssl.digest")
|
|
_M.hmac = require("resty.openssl.hmac")
|
|
_M.kdf = require("resty.openssl.kdf")
|
|
_M.pkey = require("resty.openssl.pkey")
|
|
_M.objects = require("resty.openssl.objects")
|
|
_M.rand = require("resty.openssl.rand")
|
|
_M.version = require("resty.openssl.version")
|
|
_M.x509 = require("resty.openssl.x509")
|
|
_M.altname = require("resty.openssl.x509.altname")
|
|
_M.chain = require("resty.openssl.x509.chain")
|
|
_M.csr = require("resty.openssl.x509.csr")
|
|
_M.crl = require("resty.openssl.x509.crl")
|
|
_M.extension = require("resty.openssl.x509.extension")
|
|
_M.extensions = require("resty.openssl.x509.extensions")
|
|
_M.name = require("resty.openssl.x509.name")
|
|
_M.revoked = require("resty.openssl.x509.revoked")
|
|
_M.store = require("resty.openssl.x509.store")
|
|
_M.pkcs12 = require("resty.openssl.pkcs12")
|
|
_M.ssl = require("resty.openssl.ssl")
|
|
_M.ssl_ctx = require("resty.openssl.ssl_ctx")
|
|
|
|
if OPENSSL_3X then
|
|
_M.provider = require("resty.openssl.provider")
|
|
_M.mac = require("resty.openssl.mac")
|
|
_M.ctx = require("resty.openssl.ctx")
|
|
end
|
|
|
|
_M.bignum = _M.bn
|
|
end
|
|
|
|
function _M.luaossl_compat()
|
|
_M.load_modules()
|
|
|
|
_M.csr.setSubject = _M.csr.set_subject_name
|
|
_M.csr.setPublicKey = _M.csr.set_pubkey
|
|
|
|
_M.x509.setPublicKey = _M.x509.set_pubkey
|
|
_M.x509.getPublicKey = _M.x509.get_pubkey
|
|
_M.x509.setSerial = _M.x509.set_serial_number
|
|
_M.x509.getSerial = _M.x509.get_serial_number
|
|
_M.x509.setSubject = _M.x509.set_subject_name
|
|
_M.x509.getSubject = _M.x509.get_subject_name
|
|
_M.x509.setIssuer = _M.x509.set_issuer_name
|
|
_M.x509.getIssuer = _M.x509.get_issuer_name
|
|
_M.x509.getOCSP = _M.x509.get_ocsp_url
|
|
|
|
local pkey_new = _M.pkey.new
|
|
_M.pkey.new = function(a, b)
|
|
if type(a) == "string" then
|
|
return pkey_new(a, b and unpack(b))
|
|
else
|
|
return pkey_new(a, b)
|
|
end
|
|
end
|
|
|
|
_M.cipher.encrypt = function(self, key, iv, padding)
|
|
return self, _M.cipher.init(self, key, iv, true, not padding)
|
|
end
|
|
_M.cipher.decrypt = function(self, key, iv, padding)
|
|
return self, _M.cipher.init(self, key, iv, false, not padding)
|
|
end
|
|
|
|
local digest_update = _M.digest.update
|
|
_M.digest.update = function(self, ...)
|
|
local ok, err = digest_update(self, ...)
|
|
if ok then
|
|
return self
|
|
else
|
|
return nil, err
|
|
end
|
|
end
|
|
|
|
local store_verify = _M.store.verify
|
|
_M.store.verify = function(...)
|
|
local ok, err = store_verify(...)
|
|
if err then
|
|
return false, err
|
|
else
|
|
return true, ok
|
|
end
|
|
end
|
|
|
|
local kdf_derive = _M.kdf.derive
|
|
local kdf_keys_mappings = {
|
|
iter = "pbkdf2_iter",
|
|
key = "hkdf_key",
|
|
info = "hkdf_info",
|
|
secret = "tls1_prf_secret",
|
|
seed = "tls1_prf_seed",
|
|
maxmem_bytes = "scrypt_maxmem",
|
|
N = "scrypt_N",
|
|
r = "scrypt_r",
|
|
p = "scrypt_p",
|
|
}
|
|
_M.kdf.derive = function(o)
|
|
for k1, k2 in pairs(kdf_keys_mappings) do
|
|
o[k1] = o[k2]
|
|
o[k2] = nil
|
|
end
|
|
local hkdf_mode = o.hkdf_mode
|
|
if hkdf_mode == "extract_and_expand" then
|
|
o.hkdf_mode = _M.kdf.HKDEF_MODE_EXTRACT_AND_EXPAND
|
|
elseif hkdf_mode == "extract_only" then
|
|
o.hkdf_mode = _M.kdf.HKDEF_MODE_EXTRACT_ONLY
|
|
elseif hkdf_mode == "expand_only" then
|
|
o.hkdf_mode = _M.kdf.HKDEF_MODE_EXPAND_ONLY
|
|
end
|
|
return kdf_derive(o)
|
|
end
|
|
|
|
_M.pkcs12.new = function(tbl)
|
|
local certs = {}
|
|
local passphrase = tbl.passphrase
|
|
if not tbl.key then
|
|
return nil, "key must be set"
|
|
end
|
|
for _, cert in ipairs(tbl.certs) do
|
|
if not _M.x509.istype(cert) then
|
|
return nil, "certs must contains only x509 instance"
|
|
end
|
|
if cert:check_private_key(tbl.key) then
|
|
tbl.cert = cert
|
|
else
|
|
certs[#certs+1] = cert
|
|
end
|
|
end
|
|
tbl.cacerts = certs
|
|
return _M.pkcs12.encode(tbl, passphrase)
|
|
end
|
|
|
|
_M.crl.add = _M.crl.add_revoked
|
|
_M.crl.lookupSerial = _M.crl.get_by_serial
|
|
|
|
for mod, tbl in pairs(_M) do
|
|
if type(tbl) == 'table' then
|
|
|
|
-- avoid using a same table as the iterrator will change
|
|
local new_tbl = {}
|
|
-- luaossl always error() out
|
|
for k, f in pairs(tbl) do
|
|
if type(f) == 'function' then
|
|
local of = f
|
|
new_tbl[k] = function(...)
|
|
local ret = { of(...) }
|
|
if ret and #ret > 1 and ret[#ret] then
|
|
error(mod .. "." .. k .. "(): " .. ret[#ret])
|
|
end
|
|
return unpack(ret)
|
|
end
|
|
end
|
|
end
|
|
|
|
for k, f in pairs(new_tbl) do
|
|
tbl[k] = f
|
|
end
|
|
|
|
setmetatable(tbl, {
|
|
__index = function(t, k)
|
|
local tok
|
|
-- handle special case
|
|
if k == 'toPEM' then
|
|
tok = 'to_PEM'
|
|
else
|
|
tok = k:gsub("(%l)(%u)", function(a, b) return a .. "_" .. b:lower() end)
|
|
if tok == k then
|
|
return
|
|
end
|
|
end
|
|
if type(tbl[tok]) == 'function' then
|
|
return tbl[tok]
|
|
end
|
|
end
|
|
})
|
|
end
|
|
end
|
|
|
|
-- skip error() conversion
|
|
_M.pkcs12.parse = function(p12, passphrase)
|
|
local r, err = _M.pkcs12.decode(p12, passphrase)
|
|
if err then error(err) end
|
|
return r.key, r.cert, r.cacerts
|
|
end
|
|
end
|
|
|
|
if OPENSSL_3X then
|
|
require "resty.openssl.include.evp"
|
|
local provider = require "resty.openssl.provider"
|
|
local ctx_lib = require "resty.openssl.ctx"
|
|
local fips_provider_ctx
|
|
|
|
function _M.set_fips_mode(enable, self_test)
|
|
if (not not enable) == _M.get_fips_mode() then
|
|
return true
|
|
end
|
|
|
|
if enable then
|
|
local p, err = provider.load("fips")
|
|
if not p then
|
|
return false, err
|
|
end
|
|
fips_provider_ctx = p
|
|
if self_test then
|
|
local ok, err = p:self_test()
|
|
if not ok then
|
|
return false, err
|
|
end
|
|
end
|
|
|
|
elseif fips_provider_ctx then -- disable
|
|
local p = fips_provider_ctx
|
|
fips_provider_ctx = nil
|
|
return p:unload()
|
|
end
|
|
|
|
-- set algorithm in fips mode in default ctx
|
|
-- this deny/allow non-FIPS compliant algorithms to be used from EVP interface
|
|
-- and redirect/remove redirect implementation to fips provider
|
|
if C.EVP_default_properties_enable_fips(ctx_lib.get_libctx(), enable and 1 or 0) == 0 then
|
|
return false, format_error("openssl.set_fips_mode: EVP_default_properties_enable_fips")
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
function _M.get_fips_mode()
|
|
local pok = provider.is_available("fips")
|
|
if not pok then
|
|
return false
|
|
end
|
|
|
|
return C.EVP_default_properties_is_fips_enabled(ctx_lib.get_libctx()) == 1
|
|
end
|
|
|
|
function _M.get_fips_version_text()
|
|
if not fips_provider_ctx then
|
|
return false, "FIPS mode is not enabled"
|
|
end
|
|
|
|
return fips_provider_ctx:get_params("version")
|
|
end
|
|
|
|
else
|
|
function _M.set_fips_mode(enable)
|
|
if (not not enable) == _M.get_fips_mode() then
|
|
return true
|
|
end
|
|
|
|
if C.FIPS_mode_set(enable and 1 or 0) == 0 then
|
|
return false, format_error("openssl.set_fips_mode")
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
function _M.get_fips_mode()
|
|
return C.FIPS_mode() == 1
|
|
end
|
|
|
|
function _M.get_fips_version_text()
|
|
return nil, "openssl.get_fips_version_text not supported on OpenSSL 1.1.1"
|
|
end
|
|
end
|
|
|
|
function _M.set_default_properties(props)
|
|
if not OPENSSL_3X then
|
|
return nil, "openssl.set_default_properties is only not supported from OpenSSL 3.0"
|
|
end
|
|
|
|
local ctx_lib = require "resty.openssl.ctx"
|
|
|
|
if C.EVP_set_default_properties(ctx_lib.get_libctx(), props) == 0 then
|
|
return false, format_error("openssl.EVP_set_default_properties")
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
local function list_legacy(typ, get_nid_cf)
|
|
local typ_lower = string.lower(typ:sub(5)) -- cut off EVP_
|
|
require ("resty.openssl.include.evp." .. typ_lower)
|
|
|
|
local ret = {}
|
|
local fn = ffi_cast("fake_openssl_" .. typ_lower .. "_list_fn*",
|
|
function(elem, from, to, arg)
|
|
if elem ~= nil then
|
|
local nid = get_nid_cf(elem)
|
|
table.insert(ret, ffi_str(C.OBJ_nid2sn(nid)))
|
|
end
|
|
-- from/to (renamings) are ignored
|
|
end)
|
|
C[typ .. "_do_all_sorted"](fn, nil)
|
|
fn:free()
|
|
|
|
return ret
|
|
end
|
|
|
|
local function list_provided(typ, hide_provider)
|
|
local typ_lower = string.lower(typ:sub(5)) -- cut off EVP_
|
|
local typ_ptr = typ .. "*"
|
|
require ("resty.openssl.include.evp." .. typ_lower)
|
|
local ctx_lib = require "resty.openssl.ctx"
|
|
|
|
local ret = {}
|
|
|
|
local fn = ffi_cast("fake_openssl_" .. typ_lower .. "_provided_list_fn*",
|
|
function(elem, _)
|
|
elem = ffi_cast(typ_ptr, elem)
|
|
local name = ffi_str(C[typ .. "_get0_name"](elem))
|
|
if hide_provider then
|
|
table.insert(ret, name)
|
|
else
|
|
-- alternate names are ignored, retrieve use TYPE_names_do_all
|
|
local prov = ffi_str(C.OSSL_PROVIDER_get0_name(C[typ .. "_get0_provider"](elem)))
|
|
table.insert(ret, name .. " @ " .. prov)
|
|
end
|
|
end)
|
|
|
|
C[typ .. "_do_all_provided"](ctx_lib.get_libctx(), fn, nil)
|
|
fn:free()
|
|
|
|
table.sort(ret)
|
|
return ret
|
|
end
|
|
|
|
function _M.list_cipher_algorithms(hide_provider)
|
|
require "resty.openssl.include.evp.cipher"
|
|
|
|
if OPENSSL_3X then
|
|
return list_provided("EVP_CIPHER", hide_provider)
|
|
else
|
|
return list_legacy("EVP_CIPHER", C.EVP_CIPHER_nid)
|
|
end
|
|
end
|
|
|
|
function _M.list_digest_algorithms(hide_provider)
|
|
require "resty.openssl.include.evp.md"
|
|
|
|
if OPENSSL_3X then
|
|
return list_provided("EVP_MD", hide_provider)
|
|
else
|
|
return list_legacy("EVP_MD", C.EVP_MD_type)
|
|
end
|
|
end
|
|
|
|
function _M.list_mac_algorithms(hide_provider)
|
|
if not OPENSSL_3X then
|
|
return nil, "openssl.list_mac_algorithms is only supported from OpenSSL 3.0"
|
|
end
|
|
|
|
return list_provided("EVP_MAC", hide_provider)
|
|
end
|
|
|
|
function _M.list_kdf_algorithms(hide_provider)
|
|
if not OPENSSL_3X then
|
|
return nil, "openssl.list_kdf_algorithms is only supported from OpenSSL 3.0"
|
|
end
|
|
|
|
return list_provided("EVP_KDF", hide_provider)
|
|
end
|
|
|
|
local valid_ssl_protocols = {
|
|
["SSLv3"] = 0x0300,
|
|
["TLSv1"] = 0x0301,
|
|
["TLSv1.1"] = 0x0302,
|
|
["TLSv1.2"] = 0x0303,
|
|
["TLSv1.3"] = 0x0304,
|
|
}
|
|
|
|
function _M.list_ssl_ciphers(cipher_list, ciphersuites, protocol)
|
|
local ssl_lib = require("resty.openssl.ssl")
|
|
local ssl_macro = require("resty.openssl.include.ssl")
|
|
|
|
if protocol then
|
|
if not valid_ssl_protocols[protocol] then
|
|
return nil, "unknown protocol \"" .. protocol .. "\""
|
|
end
|
|
protocol = valid_ssl_protocols[protocol]
|
|
end
|
|
|
|
local ssl_ctx = C.SSL_CTX_new(C.TLS_server_method())
|
|
if ssl_ctx == nil then
|
|
return nil, format_error("SSL_CTX_new")
|
|
end
|
|
ffi.gc(ssl_ctx, C.SSL_CTX_free)
|
|
|
|
local ssl = C.SSL_new(ssl_ctx)
|
|
if ssl == nil then
|
|
return nil, format_error("SSL_new")
|
|
end
|
|
ffi.gc(ssl, C.SSL_free)
|
|
|
|
if protocol then
|
|
if ssl_macro.SSL_set_min_proto_version(ssl, protocol) == 0 or
|
|
ssl_macro.SSL_set_max_proto_version(ssl, protocol) == 0 then
|
|
return nil, format_error("SSL_set_min/max_proto_version")
|
|
end
|
|
end
|
|
|
|
ssl = { ctx = ssl }
|
|
|
|
local ok, err
|
|
if cipher_list then
|
|
ok, err = ssl_lib.set_cipher_list(ssl, cipher_list)
|
|
if not ok then
|
|
return nil, err
|
|
end
|
|
end
|
|
|
|
if ciphersuites then
|
|
ok, err = ssl_lib.set_ciphersuites(ssl, ciphersuites)
|
|
if not ok then
|
|
return nil, err
|
|
end
|
|
end
|
|
|
|
return ssl_lib.get_ciphers(ssl)
|
|
end
|
|
|
|
return _M
|