File: //opt/imunify360-webshield/lualib/resty/openssl/x509/altname.lua
local ffi = require "ffi"
local C = ffi.C
local ffi_gc = ffi.gc
local ffi_cast = ffi.cast
local ffi_str = ffi.string
require "resty.openssl.include.x509"
require "resty.openssl.include.x509v3"
local asn1_macro = require "resty.openssl.include.asn1"
local stack_lib = require "resty.openssl.stack"
local name_lib = require "resty.openssl.x509.name"
local altname_macro = require "resty.openssl.include.x509.altname"
local _M = {}
local general_names_ptr_ct = ffi.typeof("GENERAL_NAMES*")
local STACK = "GENERAL_NAME"
local new = stack_lib.new_of(STACK)
local add = stack_lib.add_of(STACK)
local dup = stack_lib.dup_of(STACK)
local types = altname_macro.types
local AF_INET = 2
local AF_INET6 = 10
if ffi.os == "OSX" then
AF_INET6 = 30
elseif ffi.os == "BSD" then
AF_INET6 = 28
elseif ffi.os == "Windows" then
AF_INET6 = 23
end
ffi.cdef [[
typedef int socklen_t;
int inet_pton(int af, const char *restrict src, void *restrict dst);
const char *inet_ntop(int af, const void *restrict src,
char *restrict dst, socklen_t size);
]]
local ip_buffer = ffi.new("unsigned char [46]") -- 46 bytes enough for both string ipv6 and binary ipv6
-- similar to GENERAL_NAME_print, but returns value instead of print
local gn_decode = function(ctx)
local typ = ctx.type
local k = altname_macro.literals[typ]
local v
if typ == types.OtherName then
v = "OtherName:<unsupported>"
elseif typ == types.RFC822Name then
v = ffi_str(asn1_macro.ASN1_STRING_get0_data(ctx.d.rfc822Name))
elseif typ == types.DNS then
v = ffi_str(asn1_macro.ASN1_STRING_get0_data(ctx.d.dNSName))
elseif typ == types.X400 then
v = "X400:<unsupported>"
elseif typ == types.DirName then
v = name_lib.dup(ctx.d.directoryName)
elseif typ == types.EdiParty then
v = "EdiParty:<unsupported>"
elseif typ == types.URI then
v = ffi_str(asn1_macro.ASN1_STRING_get0_data(ctx.d.uniformResourceIdentifier))
elseif typ == types.IP then
v = asn1_macro.ASN1_STRING_get0_data(ctx.d.iPAddress)
local l = tonumber(C.ASN1_STRING_length(ctx.d.iPAddress))
if l ~= 4 and l ~= 16 then
error("Unknown IP address type")
end
v = C.inet_ntop(l == 4 and AF_INET or AF_INET6, v, ip_buffer, 46)
v = ffi_str(v)
elseif typ == types.RID then
v = "RID:<unsupported>"
else
error("unknown type" .. typ .. "-> " .. types.OtherName)
end
return { k, v }
end
-- shared with info_access
_M.gn_decode = gn_decode
local mt = stack_lib.mt_of(STACK, gn_decode, _M)
local mt__pairs = mt.__pairs
mt.__pairs = function(tbl)
local f = mt__pairs(tbl)
return function()
local _, e = f()
if not e then return end
return unpack(e)
end
end
function _M.new()
local ctx = new()
if ctx == nil then
return nil, "x509.altname.new: OPENSSL_sk_new_null() failed"
end
local cast = ffi_cast("GENERAL_NAMES*", ctx)
local self = setmetatable({
ctx = ctx,
cast = cast,
_is_shallow_copy = false,
}, mt)
return self, nil
end
function _M.istype(l)
return l and l.cast and ffi.istype(general_names_ptr_ct, l.cast)
end
function _M.dup(ctx)
if ctx == nil or not ffi.istype(general_names_ptr_ct, ctx) then
return nil, "x509.altname.dup: expect a GENERAL_NAMES* ctx at #1"
end
local dup_ctx, err = dup(ctx)
if dup_ctx == nil then
return nil, err
end
return setmetatable({
cast = ffi_cast(general_names_ptr_ct, dup_ctx),
ctx = dup_ctx,
-- don't let lua gc the original stack to keep its elements
_dupped_from = ctx,
_is_shallow_copy = true,
_elem_refs = {},
_elem_refs_idx = 1,
}, mt), nil
end
local function gn_set(gn, typ, value)
if type(typ) ~= 'string' then
return "x509.altname:gn_set: expect a string at #1"
end
local typ_lower = typ:lower()
if type(value) ~= 'string' then
return "x509.altname:gn_set: except a string at #2"
end
local txt = value
local gn_type = types[typ_lower]
if not gn_type then
return "x509.altname:gn_set: unknown type " .. typ
end
if gn_type == types.IP then
if C.inet_pton(AF_INET, txt, ip_buffer) == 1 then
txt = ffi_str(ip_buffer, 4)
elseif C.inet_pton(AF_INET6, txt, ip_buffer) == 1 then
txt = ffi_str(ip_buffer, 16)
else
return "x509.altname:gn_set: invalid IP address " .. txt
end
elseif gn_type ~= types.Email and
gn_type ~= types.URI and
gn_type ~= types.DNS then
return "x509.altname:gn_set: setting type " .. typ .. " is currently not supported"
end
gn.type = gn_type
local asn1_string = C.ASN1_IA5STRING_new()
if asn1_string == nil then
return "x509.altname:gn_set: ASN1_STRING_type_new() failed"
end
local code = C.ASN1_STRING_set(asn1_string, txt, #txt)
if code ~= 1 then
C.ASN1_STRING_free(asn1_string)
return "x509.altname:gn_set: ASN1_STRING_set() failed: " .. code
end
gn.d.ia5 = asn1_string
end
-- shared with info_access
_M.gn_set = gn_set
function _M:add(typ, value)
-- the stack element stays with stack
-- we shouldn't add gc handler if it's already been
-- pushed to stack. instead, rely on the gc handler
-- of the stack to release all memories
local gn = C.GENERAL_NAME_new()
if gn == nil then
return nil, "x509.altname:add: GENERAL_NAME_new() failed"
end
local err = gn_set(gn, typ, value)
if err then
C.GENERAL_NAME_free(gn)
return nil, err
end
local _, err = add(self.ctx, gn)
if err then
C.GENERAL_NAME_free(gn)
return nil, err
end
-- if the stack is duplicated, the gc handler is not pop_free
-- handle the gc by ourselves
if self._is_shallow_copy then
ffi_gc(gn, C.GENERAL_NAME_free)
self._elem_refs[self._elem_refs_idx] = gn
self._elem_refs_idx = self._elem_refs_idx + 1
end
return self
end
_M.all = function(self)
local ret = {}
local _next = mt.__pairs(self)
while true do
local k, v = _next()
if k then
ret[k] = v
else
break
end
end
return ret
end
_M.each = mt.__pairs
_M.index = mt.__index
_M.count = mt.__len
mt.__tostring = function(self)
local values = {}
local _next = mt.__pairs(self)
while true do
local k, v = _next()
if k then
table.insert(values, k .. "=" .. v)
else
break
end
end
table.sort(values)
return table.concat(values, "/")
end
_M.tostring = mt.__tostring
return _M