diff options
Diffstat (limited to 'server/resty/openssl/x509/altname.lua')
-rw-r--r-- | server/resty/openssl/x509/altname.lua | 248 |
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 |