Skip to content

Commit 083a201

Browse files
committed
feat(x509.altname) support iterate and decode over the stack
1 parent 243f40d commit 083a201

File tree

3 files changed

+289
-32
lines changed

3 files changed

+289
-32
lines changed

lib/resty/openssl/include/x509/altname.lua

+9
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,19 @@ local types = {
2020
DirName = GEN_DIRNAME,
2121
}
2222

23+
local literals = {
24+
[GEN_EMAIL] = "email",
25+
[GEN_DNS] = "DNS",
26+
[GEN_DIRNAME] = "DirName",
27+
[GEN_URI] = "URI",
28+
[GEN_IPADD] = "IP",
29+
}
30+
2331
for t, gid in pairs(types) do
2432
types[t:lower()] = gid
2533
end
2634

2735
return {
2836
types = types,
37+
literals = literals,
2938
}

lib/resty/openssl/x509/altname.lua

+118-32
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,175 @@
11
local ffi = require "ffi"
22
local C = ffi.C
3+
local ffi_gc = ffi.gc
34
local ffi_cast = ffi.cast
5+
local ffi_str = ffi.string
46

7+
require "resty.openssl.include.x509"
58
require "resty.openssl.include.x509v3"
6-
require "resty.openssl.include.asn1"
9+
local asn1_macro = require "resty.openssl.include.asn1"
710
local stack_lib = require "resty.openssl.stack"
811
local altname_macro = require "resty.openssl.include.x509.altname"
912

1013
local _M = {}
11-
local mt = { __index = _M }
14+
local mt
1215

1316
local general_names_ptr_ct = ffi.typeof("GENERAL_NAMES*")
1417

1518
local STACK = "GENERAL_NAME"
1619
local new = stack_lib.new_of(STACK)
1720
local add = stack_lib.add_of(STACK)
21+
local dup = stack_lib.dup_of(STACK)
1822

1923
local types = altname_macro.types
2024

25+
local gn_decode = function(ctx)
26+
local typ = ctx.type
27+
local k = altname_macro.literals[typ]
28+
local v
29+
if typ == types.DNS then
30+
v = ffi_str(asn1_macro.ASN1_STRING_get0_data(ctx.d.dNSName))
31+
elseif typ == types.URI then
32+
v = ffi_str(asn1_macro.ASN1_STRING_get0_data(ctx.d.uniformResourceIdentifier))
33+
elseif typ == types.RFC822Name then
34+
v = ffi_str(asn1_macro.ASN1_STRING_get0_data(ctx.d.rfc822Name))
35+
elseif typ == types.IP then
36+
error("NYI")
37+
elseif typ == types.DirName then
38+
error("NYI")
39+
else
40+
error("unknown type" .. typ)
41+
end
42+
return { k, v }
43+
end
44+
45+
-- shared with info_access
46+
_M.gn_decode = gn_decode
47+
48+
mt = stack_lib.mt_of(STACK, gn_decode, _M)
49+
local mt__pairs = mt.__pairs
50+
mt.__pairs = function(tbl)
51+
local f = mt__pairs(tbl)
52+
return function()
53+
local _, e = f()
54+
if not e then return end
55+
return unpack(e)
56+
end
57+
end
58+
2159
function _M.new()
22-
local raw = new()
23-
if raw == nil then
60+
local ctx = new()
61+
if ctx == nil then
2462
return nil, "OPENSSL_sk_new_null() failed"
2563
end
26-
local ctx = ffi_cast("GENERAL_NAMES*", raw)
64+
local cast = ffi_cast("GENERAL_NAMES*", ctx)
2765

2866
local self = setmetatable({
2967
ctx = ctx,
30-
raw = raw
68+
cast = cast,
69+
_is_dup = false,
3170
}, mt)
3271

3372
return self, nil
3473
end
3574

3675
function _M.istype(l)
37-
return l and l.ctx and ffi.istype(general_names_ptr_ct, l.ctx)
76+
return l and l.cast and ffi.istype(general_names_ptr_ct, l.cast)
3877
end
3978

40-
function _M:add(typ, value)
79+
function _M.dup(ctx)
80+
if ctx == nil or not ffi.istype(general_names_ptr_ct, ctx) then
81+
return nil, "expect a GENERAL_NAMES* ctx at #1"
82+
end
83+
ctx = dup(ctx)
84+
85+
return setmetatable({
86+
cast = ffi_cast("GENERAL_NAMES*", ctx),
87+
ctx = ctx,
88+
_is_dup = true,
89+
_elem_refs = {},
90+
_elem_refs_idx = 1,
91+
}, mt), nil
92+
end
93+
94+
local function gn_set(gn, typ, value)
4195
if not typ then
42-
return nil, "expect a string at #1"
96+
return "expect a string at #1"
4397
end
4498
typ = typ:lower()
4599
if type(value) ~= 'string' then
46-
return nil, "except a string at #2"
100+
return "except a string at #2"
47101
end
48102

49103
local txt = value
50-
local gen_type = types[typ]
51-
if not gen_type then
52-
return nil, "unknown type " .. typ
104+
local gn_type = types[typ]
105+
if not gn_type then
106+
return "unknown type " .. typ
107+
end
108+
109+
gn.type = gn_type
110+
111+
local asn1_string = C.ASN1_IA5STRING_new()
112+
if asn1_string == nil then
113+
C.GENERAL_NAME_free(gn)
114+
return "ASN1_STRING_type_new() failed"
115+
end
116+
117+
gn.d.ia5 = asn1_string
118+
119+
local code = C.ASN1_STRING_set(gn.d.ia5, txt, #txt)
120+
if code ~= 1 then
121+
C.GENERAL_NAME_free(gn)
122+
return "ASN1_STRING_set() failed: " .. code
53123
end
124+
end
125+
126+
-- shared with info_access
127+
_M.gn_set = gn_set
128+
129+
function _M:add(typ, value)
54130

55131
-- the stack element stays with stack
56132
-- we shouldn't add gc handler if it's already been
57133
-- pushed to stack. instead, rely on the gc handler
58134
-- of the stack to release all memories
59-
local gen = C.GENERAL_NAME_new()
60-
if gen == nil then
135+
local gn = C.GENERAL_NAME_new()
136+
if gn == nil then
61137
return nil, "GENERAL_NAME_new() failed"
62138
end
63139

64-
gen.type = gen_type
65-
66-
-- #define V_ASN1_IA5STRING 22
67-
local asn1_string = C.ASN1_STRING_type_new(22)
68-
if asn1_string == nil then
69-
C.GENERAL_NAME_free(gen)
70-
return nil, "ASN1_STRING_type_new() failed"
71-
end
72-
73-
gen.d.ia5 = asn1_string
74-
75-
local code = C.ASN1_STRING_set(gen.d.ia5, txt, #txt)
76-
if code ~= 1 then
77-
C.GENERAL_NAME_free(gen)
78-
return nil, "ASN1_STRING_set() failed: " .. code
140+
local err = gn_set(gn, typ, value)
141+
if err then
142+
return err
79143
end
80144

81-
local _, err = add(self.ctx, gen)
145+
local _, err = add(self.ctx, gn)
82146
if err then
83-
C.GENERAL_NAME_free(gen)
147+
C.GENERAL_NAME_free(gn)
84148
return nil, err
85149
end
150+
151+
-- if the stack is duplicated, the gc handler is not pop_free
152+
-- handle the gc by ourselves
153+
if self._is_dup then
154+
ffi_gc(gn, C.GENERAL_NAME_free)
155+
self._elem_refs[self._elem_refs_idx] = gn
156+
self._elem_refs_idx = self._elem_refs_idx + 1
157+
end
86158
return self
87159
end
88160

161+
_M.all = function(stack)
162+
local ret = {}
163+
local _next = mt.__ipairs(stack)
164+
while true do
165+
local i, e = _next()
166+
if i then
167+
ret[i] = e
168+
else
169+
break
170+
end
171+
end
172+
return ret
173+
end
174+
89175
return _M

t/openssl/x509/altname.t

+162
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
# vim:set ft= ts=4 sw=4 et fdm=marker:
2+
3+
use Test::Nginx::Socket::Lua 'no_plan';
4+
use Cwd qw(cwd);
5+
6+
7+
my $pwd = cwd();
8+
9+
our $HttpConfig = qq{
10+
lua_package_path "$pwd/t/openssl/x509/?.lua;$pwd/lib/?.lua;$pwd/lib/?/init.lua;;";
11+
};
12+
13+
14+
run_tests();
15+
16+
__DATA__
17+
=== TEST 1: Creates stack properly
18+
--- http_config eval: $::HttpConfig
19+
--- config
20+
location =/t {
21+
content_by_lua_block {
22+
local altname = require("resty.openssl.x509.altname")
23+
local c, err = altname.new()
24+
if err then
25+
ngx.log(ngx.ERR, err)
26+
return
27+
end
28+
ngx.say(#c)
29+
}
30+
}
31+
--- request
32+
GET /t
33+
--- response_body eval
34+
"0
35+
"
36+
--- no_error_log
37+
[error]
38+
39+
=== TEST 2: Adds elements to stack properly
40+
--- http_config eval: $::HttpConfig
41+
--- config
42+
location =/t {
43+
content_by_lua_block {
44+
local altname = require("resty.openssl.x509.altname")
45+
local c, err = altname.new()
46+
if err then
47+
ngx.log(ngx.ERR, err)
48+
return
49+
end
50+
for i=0,2,1 do
51+
local ok, err = c:add("DNS", string.format("%d.com", i))
52+
if err then
53+
ngx.log(ngx.ERR, err)
54+
return
55+
end
56+
end
57+
ngx.say(#c)
58+
ngx.say(#c:all())
59+
}
60+
}
61+
--- request
62+
GET /t
63+
--- response_body eval
64+
"3
65+
3
66+
"
67+
--- no_error_log
68+
[error]
69+
70+
=== TEST 3: Element can be indexed properly
71+
--- http_config eval: $::HttpConfig
72+
--- config
73+
location =/t {
74+
content_by_lua_block {
75+
local altname = require("resty.openssl.x509.altname")
76+
local c, err = altname.new()
77+
if err then
78+
ngx.log(ngx.ERR, err)
79+
return
80+
end
81+
for i=0,2,1 do
82+
local ok, err = c:add("DNS", string.format("%d.com", i))
83+
if err then
84+
ngx.log(ngx.ERR, err)
85+
return
86+
end
87+
end
88+
for k, v in pairs(c) do
89+
ngx.say(k, " ", v)
90+
end
91+
}
92+
}
93+
--- request
94+
GET /t
95+
--- response_body eval
96+
"DNS 0.com
97+
DNS 1.com
98+
DNS 2.com
99+
"
100+
--- no_error_log
101+
[error]
102+
103+
=== TEST 4: Element is duplicated when added to stack
104+
--- http_config eval: $::HttpConfig
105+
--- config
106+
location =/t {
107+
content_by_lua_block {
108+
local altname = require("resty.openssl.x509.altname")
109+
local c, err = altname.new()
110+
if err then
111+
ngx.log(ngx.ERR, err)
112+
return
113+
end
114+
local ok, err = c:add("DNS", "example.com")
115+
if err then
116+
ngx.log(ngx.ERR, err)
117+
return
118+
end
119+
cert = nil
120+
collectgarbage("collect")
121+
local k, v = unpack(c[1])
122+
ngx.say(k, " ", v)
123+
}
124+
}
125+
--- request
126+
GET /t
127+
--- response_body eval
128+
"DNS example.com
129+
"
130+
--- no_error_log
131+
[error]
132+
133+
=== TEST 5: Element is duplicated when returned
134+
--- http_config eval: $::HttpConfig
135+
--- config
136+
location =/t {
137+
content_by_lua_block {
138+
local altname = require("resty.openssl.x509.altname")
139+
local c, err = altname.new()
140+
if err then
141+
ngx.log(ngx.ERR, err)
142+
return
143+
end
144+
local ok, err = c:add("DNS", "example.com")
145+
if err then
146+
ngx.log(ngx.ERR, err)
147+
return
148+
end
149+
local cc = c[1]
150+
c = nil
151+
collectgarbage("collect")
152+
local k, v = unpack(cc)
153+
ngx.say(k, " ", v)
154+
}
155+
}
156+
--- request
157+
GET /t
158+
--- response_body eval
159+
"DNS example.com
160+
"
161+
--- no_error_log
162+
[error]

0 commit comments

Comments
 (0)