aboutsummaryrefslogtreecommitdiffstats
path: root/server/resty/openssl/x509/altname.lua
diff options
context:
space:
mode:
Diffstat (limited to 'server/resty/openssl/x509/altname.lua')
-rw-r--r--server/resty/openssl/x509/altname.lua248
1 files changed, 248 insertions, 0 deletions
diff --git a/server/resty/openssl/x509/altname.lua b/server/resty/openssl/x509/altname.lua
new file mode 100644
index 0000000..34bf9e0
--- /dev/null
+++ b/server/resty/openssl/x509/altname.lua
@@ -0,0 +1,248 @@
+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 = dup(ctx)
+
+ return setmetatable({
+ cast = ffi_cast("GENERAL_NAMES*", 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