summaryrefslogtreecommitdiffstats
path: root/server/resty/openssl/x509/store.lua
blob: 1722e4c1727caeba90d02b4a07b7730f22e17959 (plain)
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