summaryrefslogtreecommitdiffstats
path: root/server/resty/openssl/x509/name.lua
blob: f83fcc114b3f02737c418983f0f779fc3c81c51f (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
local ffi = require "ffi"
local C = ffi.C
local ffi_gc = ffi.gc
local ffi_str = ffi.string

require "resty.openssl.include.x509.name"
require "resty.openssl.include.err"
local objects_lib = require "resty.openssl.objects"
local asn1_macro = require "resty.openssl.include.asn1"

-- local MBSTRING_FLAG = 0x1000
local MBSTRING_ASC  = 0x1001 -- (MBSTRING_FLAG|1)

local _M = {}

local x509_name_ptr_ct = ffi.typeof("X509_NAME*")

-- starts from 0
local function value_at(ctx, i)
  local entry = C.X509_NAME_get_entry(ctx, i)
  local obj = C.X509_NAME_ENTRY_get_object(entry)
  local ret = objects_lib.obj2table(obj)

  local str = C.X509_NAME_ENTRY_get_data(entry)
  if str ~= nil then
    ret.blob = ffi_str(asn1_macro.ASN1_STRING_get0_data(str))
  end

  return ret
end

local function iter(tbl)
  local i = 0
  local n = tonumber(C.X509_NAME_entry_count(tbl.ctx))
  return function()
    i = i + 1
    if i <= n then
      local obj = value_at(tbl.ctx, i-1)
      return obj.sn or obj.ln or obj.id, obj
    end
  end
end

local mt = {
  __index = _M,
  __pairs = iter,
  __len = function(tbl) return tonumber(C.X509_NAME_entry_count(tbl.ctx)) end,
}

function _M.new()
  local ctx = C.X509_NAME_new()
  if ctx == nil then
    return nil, "x509.name.new: X509_NAME_new() failed"
  end
  ffi_gc(ctx, C.X509_NAME_free)

  local self = setmetatable({
    ctx = ctx,
  }, mt)

  return self, nil
end

function _M.istype(l)
  return l and l.ctx and ffi.istype(x509_name_ptr_ct, l.ctx)
end

function _M.dup(ctx)
  if not ffi.istype(x509_name_ptr_ct, ctx) then
    return nil, "x509.name.dup: expect a x509.name ctx at #1, got " .. type(ctx)
  end
  local ctx = C.X509_NAME_dup(ctx)
  ffi_gc(ctx, C.X509_NAME_free)

  local self = setmetatable({
    ctx = ctx,
  }, mt)

  return self, nil
end

function _M:add(nid, txt)
  local asn1 = C.OBJ_txt2obj(nid, 0)
  if asn1 == nil then
    -- clean up error occurs during OBJ_txt2*
    C.ERR_clear_error()
    return nil, "x509.name:add: invalid NID text " .. (nid or "nil")
  end

  local code = C.X509_NAME_add_entry_by_OBJ(self.ctx, asn1, MBSTRING_ASC, txt, #txt, -1, 0)
  C.ASN1_OBJECT_free(asn1)

  if code ~= 1 then
    return nil, "x509.name:add: X509_NAME_add_entry_by_OBJ() failed"
  end

  return self
end

function _M:find(nid, last_pos)
  local asn1 = C.OBJ_txt2obj(nid, 0)
  if asn1 == nil then
    -- clean up error occurs during OBJ_txt2*
    C.ERR_clear_error()
    return nil, nil, "x509.name:find: invalid NID text " .. (nid or "nil")
  end
  -- make 1-index array to 0-index
  last_pos = (last_pos or 0) - 1

  local pos = C.X509_NAME_get_index_by_OBJ(self.ctx, asn1, last_pos)
  if pos == -1 then
    return nil
  end

  C.ASN1_OBJECT_free(asn1)

  return value_at(self.ctx, pos), pos+1
end

-- fallback function to iterate if LUAJIT_ENABLE_LUA52COMPAT not enabled
function _M:all()
  local ret = {}
  local _next = iter(self)
  while true do
    local k, obj = _next()
    if obj then
      ret[k] = obj
    else
      break
    end
  end
  return ret
end

function _M:each()
  return iter(self)
end

mt.__tostring = function(self)
  local values = {}
  local _next = iter(self)
  while true do
    local k, v = _next()
    if k then
      table.insert(values, k .. "=" .. v.blob)
    else
      break
    end
  end
  table.sort(values)
  return table.concat(values, "/")
end

_M.tostring = mt.__tostring

return _M