Software /
code /
prosody
Annotate
util/x509.lua @ 9912:601f9781a605
mod_csi_simple: Count buffered items and flush when it reaches configured limit
In this mode, stanzas have been serialized to strings in the internal
net.server buffer, so it is difficult to count them after the fact.
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Sun, 24 Mar 2019 18:33:38 +0100 |
parent | 9907:54e36a8677bc |
child | 10255:8e8d3b3a55da |
rev | line source |
---|---|
3651 | 1 -- Prosody IM |
2 -- Copyright (C) 2010 Matthew Wild | |
3 -- Copyright (C) 2010 Paul Aurich | |
4 -- | |
5 -- This project is MIT/X11 licensed. Please see the | |
6 -- COPYING file in the source package for more information. | |
7 -- | |
8 | |
9 -- TODO: I feel a fair amount of this logic should be integrated into Luasec, | |
10 -- so that everyone isn't re-inventing the wheel. Dependencies on | |
11 -- IDN libraries complicate that. | |
12 | |
13 | |
4330
520fcb333cba
util.x509: Update references to published RFCs
Paul Aurich <paul@darkrain42.org>
parents:
3735
diff
changeset
|
14 -- [TLS-CERTS] - http://tools.ietf.org/html/rfc6125 |
520fcb333cba
util.x509: Update references to published RFCs
Paul Aurich <paul@darkrain42.org>
parents:
3735
diff
changeset
|
15 -- [XMPP-CORE] - http://tools.ietf.org/html/rfc6120 |
3651 | 16 -- [SRV-ID] - http://tools.ietf.org/html/rfc4985 |
17 -- [IDNA] - http://tools.ietf.org/html/rfc5890 | |
18 -- [LDAP] - http://tools.ietf.org/html/rfc4519 | |
19 -- [PKIX] - http://tools.ietf.org/html/rfc5280 | |
20 | |
21 local nameprep = require "util.encodings".stringprep.nameprep; | |
22 local idna_to_ascii = require "util.encodings".idna.to_ascii; | |
9907
54e36a8677bc
util.x509: Add function that extracts usable names from a certificate
Kim Alvefur <zash@zash.se>
parents:
8555
diff
changeset
|
23 local idna_to_unicode = require "util.encodings".idna.to_unicode; |
6152
fbab74c28e31
util.x509: And functions for converting between DER and PEM
Kim Alvefur <zash@zash.se>
parents:
5845
diff
changeset
|
24 local base64 = require "util.encodings".base64; |
3735
40b54c46a14c
util.x509: "certverification" -> "x509".
Waqas Hussain <waqas20@gmail.com>
parents:
3733
diff
changeset
|
25 local log = require "util.logger".init("x509"); |
4486
f04db5e7e90d
user.x509: Add some utility functions for generating OpenSSL configs
Kim Alvefur <zash@zash.se>
parents:
4330
diff
changeset
|
26 local s_format = string.format; |
3651 | 27 |
6777
5de6b93d0190
util.*: Remove use of module() function, make all module functions local and return them in a table at the end
Kim Alvefur <zash@zash.se>
parents:
6708
diff
changeset
|
28 local _ENV = nil; |
8555
4f0f5b49bb03
vairious: Add annotation when an empty environment is set [luacheck]
Kim Alvefur <zash@zash.se>
parents:
6777
diff
changeset
|
29 -- luacheck: std none |
3651 | 30 |
31 local oid_commonname = "2.5.4.3"; -- [LDAP] 2.3 | |
32 local oid_subjectaltname = "2.5.29.17"; -- [PKIX] 4.2.1.6 | |
33 local oid_xmppaddr = "1.3.6.1.5.5.7.8.5"; -- [XMPP-CORE] | |
34 local oid_dnssrv = "1.3.6.1.5.5.7.8.7"; -- [SRV-ID] | |
35 | |
36 -- Compare a hostname (possibly international) with asserted names | |
37 -- extracted from a certificate. | |
38 -- This function follows the rules laid out in | |
4330
520fcb333cba
util.x509: Update references to published RFCs
Paul Aurich <paul@darkrain42.org>
parents:
3735
diff
changeset
|
39 -- sections 6.4.1 and 6.4.2 of [TLS-CERTS] |
3651 | 40 -- |
41 -- A wildcard ("*") all by itself is allowed only as the left-most label | |
42 local function compare_dnsname(host, asserted_names) | |
43 -- TODO: Sufficient normalization? Review relevant specs. | |
44 local norm_host = idna_to_ascii(host) | |
45 if norm_host == nil then | |
46 log("info", "Host %s failed IDNA ToASCII operation", host) | |
47 return false | |
48 end | |
49 | |
50 norm_host = norm_host:lower() | |
51 | |
52 local host_chopped = norm_host:gsub("^[^.]+%.", "") -- everything after the first label | |
53 | |
54 for i=1,#asserted_names do | |
55 local name = asserted_names[i] | |
56 if norm_host == name:lower() then | |
57 log("debug", "Cert dNSName %s matched hostname", name); | |
58 return true | |
59 end | |
60 | |
61 -- Allow the left most label to be a "*" | |
62 if name:match("^%*%.") then | |
63 local rest_name = name:gsub("^[^.]+%.", "") | |
64 if host_chopped == rest_name:lower() then | |
65 log("debug", "Cert dNSName %s matched hostname", name); | |
66 return true | |
67 end | |
68 end | |
69 end | |
70 | |
71 return false | |
72 end | |
73 | |
74 -- Compare an XMPP domain name with the asserted id-on-xmppAddr | |
75 -- identities extracted from a certificate. Both are UTF8 strings. | |
76 -- | |
77 -- Per [XMPP-CORE], matches against asserted identities don't include | |
78 -- wildcards, so we just do a normalize on both and then a string comparison | |
79 -- | |
80 -- TODO: Support for full JIDs? | |
81 local function compare_xmppaddr(host, asserted_names) | |
82 local norm_host = nameprep(host) | |
83 | |
84 for i=1,#asserted_names do | |
85 local name = asserted_names[i] | |
86 | |
87 -- We only want to match against bare domains right now, not | |
88 -- those crazy full-er JIDs. | |
89 if name:match("[@/]") then | |
90 log("debug", "Ignoring xmppAddr %s because it's not a bare domain", name) | |
91 else | |
92 local norm_name = nameprep(name) | |
93 if norm_name == nil then | |
94 log("info", "Ignoring xmppAddr %s, failed nameprep!", name) | |
95 else | |
96 if norm_host == norm_name then | |
97 log("debug", "Cert xmppAddr %s matched hostname", name) | |
98 return true | |
99 end | |
100 end | |
101 end | |
102 end | |
103 | |
104 return false | |
105 end | |
106 | |
107 -- Compare a host + service against the asserted id-on-dnsSRV (SRV-ID) | |
108 -- identities extracted from a certificate. | |
109 -- | |
110 -- Per [SRV-ID], the asserted identities will be encoded in ASCII via ToASCII. | |
111 -- Comparison is done case-insensitively, and a wildcard ("*") all by itself | |
112 -- is allowed only as the left-most non-service label. | |
113 local function compare_srvname(host, service, asserted_names) | |
114 local norm_host = idna_to_ascii(host) | |
115 if norm_host == nil then | |
116 log("info", "Host %s failed IDNA ToASCII operation", host); | |
117 return false | |
118 end | |
119 | |
120 -- Service names start with a "_" | |
121 if service:match("^_") == nil then service = "_"..service end | |
122 | |
123 norm_host = norm_host:lower(); | |
124 local host_chopped = norm_host:gsub("^[^.]+%.", "") -- everything after the first label | |
125 | |
126 for i=1,#asserted_names do | |
127 local asserted_service, name = asserted_names[i]:match("^(_[^.]+)%.(.*)"); | |
128 if service == asserted_service then | |
129 if norm_host == name:lower() then | |
130 log("debug", "Cert SRVName %s matched hostname", name); | |
131 return true; | |
132 end | |
133 | |
134 -- Allow the left most label to be a "*" | |
135 if name:match("^%*%.") then | |
136 local rest_name = name:gsub("^[^.]+%.", "") | |
137 if host_chopped == rest_name:lower() then | |
138 log("debug", "Cert SRVName %s matched hostname", name) | |
139 return true | |
140 end | |
141 end | |
142 if norm_host == name:lower() then | |
143 log("debug", "Cert SRVName %s matched hostname", name); | |
144 return true | |
145 end | |
146 end | |
147 end | |
148 | |
149 return false | |
150 end | |
151 | |
6777
5de6b93d0190
util.*: Remove use of module() function, make all module functions local and return them in a table at the end
Kim Alvefur <zash@zash.se>
parents:
6708
diff
changeset
|
152 local function verify_identity(host, service, cert) |
6708
d2beb98ece29
util.x509: Tell LuaSec we want UTF-8 data
Kim Alvefur <zash@zash.se>
parents:
6153
diff
changeset
|
153 if cert.setencode then |
d2beb98ece29
util.x509: Tell LuaSec we want UTF-8 data
Kim Alvefur <zash@zash.se>
parents:
6153
diff
changeset
|
154 cert:setencode("utf8"); |
d2beb98ece29
util.x509: Tell LuaSec we want UTF-8 data
Kim Alvefur <zash@zash.se>
parents:
6153
diff
changeset
|
155 end |
3651 | 156 local ext = cert:extensions() |
157 if ext[oid_subjectaltname] then | |
158 local sans = ext[oid_subjectaltname]; | |
159 | |
4330
520fcb333cba
util.x509: Update references to published RFCs
Paul Aurich <paul@darkrain42.org>
parents:
3735
diff
changeset
|
160 -- Per [TLS-CERTS] 6.3, 6.4.4, "a client MUST NOT seek a match for a |
3651 | 161 -- reference identifier if the presented identifiers include a DNS-ID |
162 -- SRV-ID, URI-ID, or any application-specific identifier types" | |
163 local had_supported_altnames = false | |
164 | |
165 if sans[oid_xmppaddr] then | |
166 had_supported_altnames = true | |
5845
c48f717c2fd6
util.x509: Only compare identity with oid-on-xmppAddr for XMPP services
Kim Alvefur <zash@zash.se>
parents:
4825
diff
changeset
|
167 if service == "_xmpp-client" or service == "_xmpp-server" then |
c48f717c2fd6
util.x509: Only compare identity with oid-on-xmppAddr for XMPP services
Kim Alvefur <zash@zash.se>
parents:
4825
diff
changeset
|
168 if compare_xmppaddr(host, sans[oid_xmppaddr]) then return true end |
c48f717c2fd6
util.x509: Only compare identity with oid-on-xmppAddr for XMPP services
Kim Alvefur <zash@zash.se>
parents:
4825
diff
changeset
|
169 end |
3651 | 170 end |
171 | |
172 if sans[oid_dnssrv] then | |
173 had_supported_altnames = true | |
174 -- Only check srvNames if the caller specified a service | |
175 if service and compare_srvname(host, service, sans[oid_dnssrv]) then return true end | |
176 end | |
177 | |
178 if sans["dNSName"] then | |
179 had_supported_altnames = true | |
180 if compare_dnsname(host, sans["dNSName"]) then return true end | |
181 end | |
182 | |
183 -- We don't need URIs, but [TLS-CERTS] is clear. | |
184 if sans["uniformResourceIdentifier"] then | |
185 had_supported_altnames = true | |
186 end | |
187 | |
188 if had_supported_altnames then return false end | |
189 end | |
190 | |
191 -- Extract a common name from the certificate, and check it as if it were | |
192 -- a dNSName subjectAltName (wildcards may apply for, and receive, | |
193 -- cat treats) | |
194 -- | |
4330
520fcb333cba
util.x509: Update references to published RFCs
Paul Aurich <paul@darkrain42.org>
parents:
3735
diff
changeset
|
195 -- Per [TLS-CERTS] 1.8, a CN-ID is the Common Name from a cert subject |
3651 | 196 -- which has one and only one Common Name |
197 local subject = cert:subject() | |
198 local cn = nil | |
199 for i=1,#subject do | |
200 local dn = subject[i] | |
201 if dn["oid"] == oid_commonname then | |
202 if cn then | |
203 log("info", "Certificate has multiple common names") | |
204 return false | |
205 end | |
206 | |
207 cn = dn["value"]; | |
208 end | |
209 end | |
210 | |
211 if cn then | |
4330
520fcb333cba
util.x509: Update references to published RFCs
Paul Aurich <paul@darkrain42.org>
parents:
3735
diff
changeset
|
212 -- Per [TLS-CERTS] 6.4.4, follow the comparison rules for dNSName SANs. |
3651 | 213 return compare_dnsname(host, { cn }) |
214 end | |
215 | |
216 -- If all else fails, well, why should we be any different? | |
217 return false | |
218 end | |
219 | |
9907
54e36a8677bc
util.x509: Add function that extracts usable names from a certificate
Kim Alvefur <zash@zash.se>
parents:
8555
diff
changeset
|
220 -- TODO Support other SANs |
54e36a8677bc
util.x509: Add function that extracts usable names from a certificate
Kim Alvefur <zash@zash.se>
parents:
8555
diff
changeset
|
221 local function get_identities(cert) --> set of names |
54e36a8677bc
util.x509: Add function that extracts usable names from a certificate
Kim Alvefur <zash@zash.se>
parents:
8555
diff
changeset
|
222 if cert.setencode then |
54e36a8677bc
util.x509: Add function that extracts usable names from a certificate
Kim Alvefur <zash@zash.se>
parents:
8555
diff
changeset
|
223 cert:setencode("utf8"); |
54e36a8677bc
util.x509: Add function that extracts usable names from a certificate
Kim Alvefur <zash@zash.se>
parents:
8555
diff
changeset
|
224 end |
54e36a8677bc
util.x509: Add function that extracts usable names from a certificate
Kim Alvefur <zash@zash.se>
parents:
8555
diff
changeset
|
225 |
54e36a8677bc
util.x509: Add function that extracts usable names from a certificate
Kim Alvefur <zash@zash.se>
parents:
8555
diff
changeset
|
226 local names = {}; |
54e36a8677bc
util.x509: Add function that extracts usable names from a certificate
Kim Alvefur <zash@zash.se>
parents:
8555
diff
changeset
|
227 |
54e36a8677bc
util.x509: Add function that extracts usable names from a certificate
Kim Alvefur <zash@zash.se>
parents:
8555
diff
changeset
|
228 local ext = cert:extensions(); |
54e36a8677bc
util.x509: Add function that extracts usable names from a certificate
Kim Alvefur <zash@zash.se>
parents:
8555
diff
changeset
|
229 local sans = ext[oid_subjectaltname]; |
54e36a8677bc
util.x509: Add function that extracts usable names from a certificate
Kim Alvefur <zash@zash.se>
parents:
8555
diff
changeset
|
230 if sans and sans["dNSName"] then |
54e36a8677bc
util.x509: Add function that extracts usable names from a certificate
Kim Alvefur <zash@zash.se>
parents:
8555
diff
changeset
|
231 for i = 1, #sans["dNSName"] do |
54e36a8677bc
util.x509: Add function that extracts usable names from a certificate
Kim Alvefur <zash@zash.se>
parents:
8555
diff
changeset
|
232 names[ idna_to_unicode(sans["dNSName"][i]) ] = true; |
54e36a8677bc
util.x509: Add function that extracts usable names from a certificate
Kim Alvefur <zash@zash.se>
parents:
8555
diff
changeset
|
233 end |
54e36a8677bc
util.x509: Add function that extracts usable names from a certificate
Kim Alvefur <zash@zash.se>
parents:
8555
diff
changeset
|
234 end |
54e36a8677bc
util.x509: Add function that extracts usable names from a certificate
Kim Alvefur <zash@zash.se>
parents:
8555
diff
changeset
|
235 |
54e36a8677bc
util.x509: Add function that extracts usable names from a certificate
Kim Alvefur <zash@zash.se>
parents:
8555
diff
changeset
|
236 local subject = cert:subject(); |
54e36a8677bc
util.x509: Add function that extracts usable names from a certificate
Kim Alvefur <zash@zash.se>
parents:
8555
diff
changeset
|
237 for i = 1, #subject do |
54e36a8677bc
util.x509: Add function that extracts usable names from a certificate
Kim Alvefur <zash@zash.se>
parents:
8555
diff
changeset
|
238 local dn = subject[i]; |
54e36a8677bc
util.x509: Add function that extracts usable names from a certificate
Kim Alvefur <zash@zash.se>
parents:
8555
diff
changeset
|
239 if dn.oid == oid_commonname and nameprep(dn.value) then |
54e36a8677bc
util.x509: Add function that extracts usable names from a certificate
Kim Alvefur <zash@zash.se>
parents:
8555
diff
changeset
|
240 names[dn.value] = true; |
54e36a8677bc
util.x509: Add function that extracts usable names from a certificate
Kim Alvefur <zash@zash.se>
parents:
8555
diff
changeset
|
241 end |
54e36a8677bc
util.x509: Add function that extracts usable names from a certificate
Kim Alvefur <zash@zash.se>
parents:
8555
diff
changeset
|
242 end |
54e36a8677bc
util.x509: Add function that extracts usable names from a certificate
Kim Alvefur <zash@zash.se>
parents:
8555
diff
changeset
|
243 return names; |
54e36a8677bc
util.x509: Add function that extracts usable names from a certificate
Kim Alvefur <zash@zash.se>
parents:
8555
diff
changeset
|
244 end |
54e36a8677bc
util.x509: Add function that extracts usable names from a certificate
Kim Alvefur <zash@zash.se>
parents:
8555
diff
changeset
|
245 |
6152
fbab74c28e31
util.x509: And functions for converting between DER and PEM
Kim Alvefur <zash@zash.se>
parents:
5845
diff
changeset
|
246 local pat = "%-%-%-%-%-BEGIN ([A-Z ]+)%-%-%-%-%-\r?\n".. |
fbab74c28e31
util.x509: And functions for converting between DER and PEM
Kim Alvefur <zash@zash.se>
parents:
5845
diff
changeset
|
247 "([0-9A-Za-z+/=\r\n]*)\r?\n%-%-%-%-%-END %1%-%-%-%-%-"; |
fbab74c28e31
util.x509: And functions for converting between DER and PEM
Kim Alvefur <zash@zash.se>
parents:
5845
diff
changeset
|
248 |
6777
5de6b93d0190
util.*: Remove use of module() function, make all module functions local and return them in a table at the end
Kim Alvefur <zash@zash.se>
parents:
6708
diff
changeset
|
249 local function pem2der(pem) |
6152
fbab74c28e31
util.x509: And functions for converting between DER and PEM
Kim Alvefur <zash@zash.se>
parents:
5845
diff
changeset
|
250 local typ, data = pem:match(pat); |
fbab74c28e31
util.x509: And functions for converting between DER and PEM
Kim Alvefur <zash@zash.se>
parents:
5845
diff
changeset
|
251 if typ and data then |
fbab74c28e31
util.x509: And functions for converting between DER and PEM
Kim Alvefur <zash@zash.se>
parents:
5845
diff
changeset
|
252 return base64.decode(data), typ; |
fbab74c28e31
util.x509: And functions for converting between DER and PEM
Kim Alvefur <zash@zash.se>
parents:
5845
diff
changeset
|
253 end |
fbab74c28e31
util.x509: And functions for converting between DER and PEM
Kim Alvefur <zash@zash.se>
parents:
5845
diff
changeset
|
254 end |
fbab74c28e31
util.x509: And functions for converting between DER and PEM
Kim Alvefur <zash@zash.se>
parents:
5845
diff
changeset
|
255 |
fbab74c28e31
util.x509: And functions for converting between DER and PEM
Kim Alvefur <zash@zash.se>
parents:
5845
diff
changeset
|
256 local wrap = ('.'):rep(64); |
fbab74c28e31
util.x509: And functions for converting between DER and PEM
Kim Alvefur <zash@zash.se>
parents:
5845
diff
changeset
|
257 local envelope = "-----BEGIN %s-----\n%s\n-----END %s-----\n" |
fbab74c28e31
util.x509: And functions for converting between DER and PEM
Kim Alvefur <zash@zash.se>
parents:
5845
diff
changeset
|
258 |
6777
5de6b93d0190
util.*: Remove use of module() function, make all module functions local and return them in a table at the end
Kim Alvefur <zash@zash.se>
parents:
6708
diff
changeset
|
259 local function der2pem(data, typ) |
6152
fbab74c28e31
util.x509: And functions for converting between DER and PEM
Kim Alvefur <zash@zash.se>
parents:
5845
diff
changeset
|
260 typ = typ and typ:upper() or "CERTIFICATE"; |
fbab74c28e31
util.x509: And functions for converting between DER and PEM
Kim Alvefur <zash@zash.se>
parents:
5845
diff
changeset
|
261 data = base64.encode(data); |
fbab74c28e31
util.x509: And functions for converting between DER and PEM
Kim Alvefur <zash@zash.se>
parents:
5845
diff
changeset
|
262 return s_format(envelope, typ, data:gsub(wrap, '%0\n', (#data-1)/64), typ); |
fbab74c28e31
util.x509: And functions for converting between DER and PEM
Kim Alvefur <zash@zash.se>
parents:
5845
diff
changeset
|
263 end |
fbab74c28e31
util.x509: And functions for converting between DER and PEM
Kim Alvefur <zash@zash.se>
parents:
5845
diff
changeset
|
264 |
6777
5de6b93d0190
util.*: Remove use of module() function, make all module functions local and return them in a table at the end
Kim Alvefur <zash@zash.se>
parents:
6708
diff
changeset
|
265 return { |
5de6b93d0190
util.*: Remove use of module() function, make all module functions local and return them in a table at the end
Kim Alvefur <zash@zash.se>
parents:
6708
diff
changeset
|
266 verify_identity = verify_identity; |
9907
54e36a8677bc
util.x509: Add function that extracts usable names from a certificate
Kim Alvefur <zash@zash.se>
parents:
8555
diff
changeset
|
267 get_identities = get_identities; |
6777
5de6b93d0190
util.*: Remove use of module() function, make all module functions local and return them in a table at the end
Kim Alvefur <zash@zash.se>
parents:
6708
diff
changeset
|
268 pem2der = pem2der; |
5de6b93d0190
util.*: Remove use of module() function, make all module functions local and return them in a table at the end
Kim Alvefur <zash@zash.se>
parents:
6708
diff
changeset
|
269 der2pem = der2pem; |
5de6b93d0190
util.*: Remove use of module() function, make all module functions local and return them in a table at the end
Kim Alvefur <zash@zash.se>
parents:
6708
diff
changeset
|
270 }; |