1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
|
local ffi = require "ffi"
local C = ffi.C
local ffi_gc = ffi.gc
local ffi_str = ffi.string
local bor = bit.bor
local x509_vfy_macro = require "resty.openssl.include.x509_vfy"
local x509_lib = require "resty.openssl.x509"
local chain_lib = require "resty.openssl.x509.chain"
local crl_lib = require "resty.openssl.x509.crl"
local ctx_lib = require "resty.openssl.ctx"
local format_error = require("resty.openssl.err").format_all_error
local format_all_error = require("resty.openssl.err").format_error
local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
local _M = {}
local mt = { __index = _M }
_M.verify_flags = x509_vfy_macro.verify_flags
local x509_store_ptr_ct = ffi.typeof('X509_STORE*')
function _M.new()
local ctx = C.X509_STORE_new()
if ctx == nil then
return nil, "x509.store.new: X509_STORE_new() failed"
end
ffi_gc(ctx, C.X509_STORE_free)
local self = setmetatable({
ctx = ctx,
_elem_refs = {},
_elem_refs_idx = 1,
}, mt)
return self, nil
end
function _M.istype(l)
return l and l.ctx and ffi.istype(x509_store_ptr_ct, l.ctx)
end
function _M:use_default(properties)
if x509_vfy_macro.X509_STORE_set_default_paths(self.ctx, ctx_lib.get_libctx(), properties) ~= 1 then
return false, format_all_error("x509.store:use_default")
end
return true
end
function _M:add(item)
local dup
local err
if x509_lib.istype(item) then
dup = C.X509_dup(item.ctx)
if dup == nil then
return false, "x509.store:add: X509_dup() failed"
end
-- ref counter of dup is increased by 1
if C.X509_STORE_add_cert(self.ctx, dup) ~= 1 then
err = format_all_error("x509.store:add: X509_STORE_add_cert")
end
-- decrease the dup ctx ref count immediately to make leak test happy
C.X509_free(dup)
elseif crl_lib.istype(item) then
dup = C.X509_CRL_dup(item.ctx)
if dup == nil then
return false, "x509.store:add: X509_CRL_dup() failed"
end
-- ref counter of dup is increased by 1
if C.X509_STORE_add_crl(self.ctx, dup) ~= 1 then
err = format_all_error("x509.store:add: X509_STORE_add_crl")
end
-- define X509_V_FLAG_CRL_CHECK 0x4
-- enables CRL checking for the certificate chain leaf certificate.
-- An error occurs if a suitable CRL cannot be found.
-- Note: this does not check for certificates in the chain.
if C.X509_STORE_set_flags(self.ctx, 0x4) ~= 1 then
return false, format_error("x509.store:add: X509_STORE_set_flags")
end
-- decrease the dup ctx ref count immediately to make leak test happy
C.X509_CRL_free(dup)
else
return false, "x509.store:add: expect an x509 or crl instance at #1"
end
if err then
return false, err
end
-- X509_STORE doesn't have stack gc handler, we need to gc by ourselves
self._elem_refs[self._elem_refs_idx] = dup
self._elem_refs_idx = self._elem_refs_idx + 1
return true
end
function _M:load_file(path, properties)
if type(path) ~= "string" then
return false, "x509.store:load_file: expect a string at #1"
else
if x509_vfy_macro.X509_STORE_load_locations(self.ctx, path, nil,
ctx_lib.get_libctx(), properties) ~= 1 then
return false, format_all_error("x509.store:load_file")
end
end
return true
end
function _M:load_directory(path, properties)
if type(path) ~= "string" then
return false, "x509.store:load_directory expect a string at #1"
else
if x509_vfy_macro.X509_STORE_load_locations(self.ctx, nil, path,
ctx_lib.get_libctx(), properties) ~= 1 then
return false, format_all_error("x509.store:load_directory")
end
end
return true
end
function _M:set_depth(depth)
depth = depth and tonumber(depth)
if not depth then
return nil, "x509.store:set_depth: expect a number at #1"
end
if C.X509_STORE_set_depth(self.ctx, depth) ~= 1 then
return false, format_error("x509.store:set_depth")
end
return true
end
function _M:set_purpose(purpose)
if type(purpose) ~= "string" then
return nil, "x509.store:set_purpose: expect a string at #1"
end
local pchar = ffi.new("char[?]", #purpose, purpose)
local idx = C.X509_PURPOSE_get_by_sname(pchar)
idx = tonumber(idx)
if idx == -1 then
return false, "invalid purpose \"" .. purpose .. "\""
end
local purp = C.X509_PURPOSE_get0(idx)
local i = C.X509_PURPOSE_get_id(purp)
if C.X509_STORE_set_purpose(self.ctx, i) ~= 1 then
return false, format_error("x509.store:set_purpose: X509_STORE_set_purpose")
end
return true
end
function _M:set_flags(...)
local flag = 0
for _, f in ipairs({...}) do
flag = bor(flag, f)
end
if C.X509_STORE_set_flags(self.ctx, flag) ~= 1 then
return false, format_error("x509.store:set_flags: X509_STORE_set_flags")
end
return true
end
function _M:verify(x509, chain, return_chain, properties, verify_method)
if not x509_lib.istype(x509) then
return nil, "x509.store:verify: expect a x509 instance at #1"
elseif chain and not chain_lib.istype(chain) then
return nil, "x509.store:verify: expect a x509.chain instance at #1"
end
local ctx
if OPENSSL_3X then
ctx = C.X509_STORE_CTX_new_ex(ctx_lib.get_libctx(), properties)
else
ctx = C.X509_STORE_CTX_new()
end
if ctx == nil then
return nil, "x509.store:verify: X509_STORE_CTX_new() failed"
end
ffi_gc(ctx, C.X509_STORE_CTX_free)
local chain_dup_ctx
if chain then
local chain_dup, err = chain_lib.dup(chain.ctx)
if err then
return nil, err
end
chain_dup_ctx = chain_dup.ctx
end
if C.X509_STORE_CTX_init(ctx, self.ctx, x509.ctx, chain_dup_ctx) ~= 1 then
return nil, format_error("x509.store:verify: X509_STORE_CTX_init")
end
if verify_method and C.X509_STORE_CTX_set_default(ctx, verify_method) ~= 1 then
return nil, "x509.store:verify: invalid verify_method \"" .. verify_method .. "\""
end
local code = C.X509_verify_cert(ctx)
if code == 1 then -- verified
if not return_chain then
return true, nil
end
local ret_chain_ctx = x509_vfy_macro.X509_STORE_CTX_get0_chain(ctx)
return chain_lib.dup(ret_chain_ctx)
elseif code == 0 then -- unverified
local vfy_code = C.X509_STORE_CTX_get_error(ctx)
return nil, ffi_str(C.X509_verify_cert_error_string(vfy_code))
end
-- error
return nil, format_error("x509.store:verify: X509_verify_cert", code)
end
return _M
|