bunkerweb/lib/resty/openssl.lua
Théophile Diot 411cd2df5a Squashed 'src/deps/src/lua-resty-openssl/' changes from e56da6c5f..529f0c5ad
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
2024-07-24 12:22:21 +01:00

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