Software /
code /
prosody
Annotate
util/x509.lua @ 7567:495de404a8ae
ejabberdsql2prosody: rename variable 'host' to prevent shadowing upvalue [luacheck]
Functions roster(), roster_pending(), roster_group(), private_storage() and
offline_msg() have argument named "host", which used to shadow upvalue of this
variable before this change. Instead of renaming this argument, let's rename
the variable to match what the script says in usage:
Usage: ejabberdsql2prosody.lua filename.txt hostname
author | Anton Shestakov <av6@dwimlabs.net> |
---|---|
date | Fri, 12 Aug 2016 13:44:47 +0800 |
parent | 6777:5de6b93d0190 |
child | 8555:4f0f5b49bb03 |
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; | |
6152
fbab74c28e31
util.x509: And functions for converting between DER and PEM
Kim Alvefur <zash@zash.se>
parents:
5845
diff
changeset
|
23 local base64 = require "util.encodings".base64; |
3735
40b54c46a14c
util.x509: "certverification" -> "x509".
Waqas Hussain <waqas20@gmail.com>
parents:
3733
diff
changeset
|
24 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
|
25 local s_format = string.format; |
3651 | 26 |
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
|
27 local _ENV = nil; |
3651 | 28 |
29 local oid_commonname = "2.5.4.3"; -- [LDAP] 2.3 | |
30 local oid_subjectaltname = "2.5.29.17"; -- [PKIX] 4.2.1.6 | |
31 local oid_xmppaddr = "1.3.6.1.5.5.7.8.5"; -- [XMPP-CORE] | |
32 local oid_dnssrv = "1.3.6.1.5.5.7.8.7"; -- [SRV-ID] | |
33 | |
34 -- Compare a hostname (possibly international) with asserted names | |
35 -- extracted from a certificate. | |
36 -- 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
|
37 -- sections 6.4.1 and 6.4.2 of [TLS-CERTS] |
3651 | 38 -- |
39 -- A wildcard ("*") all by itself is allowed only as the left-most label | |
40 local function compare_dnsname(host, asserted_names) | |
41 -- TODO: Sufficient normalization? Review relevant specs. | |
42 local norm_host = idna_to_ascii(host) | |
43 if norm_host == nil then | |
44 log("info", "Host %s failed IDNA ToASCII operation", host) | |
45 return false | |
46 end | |
47 | |
48 norm_host = norm_host:lower() | |
49 | |
50 local host_chopped = norm_host:gsub("^[^.]+%.", "") -- everything after the first label | |
51 | |
52 for i=1,#asserted_names do | |
53 local name = asserted_names[i] | |
54 if norm_host == name:lower() then | |
55 log("debug", "Cert dNSName %s matched hostname", name); | |
56 return true | |
57 end | |
58 | |
59 -- Allow the left most label to be a "*" | |
60 if name:match("^%*%.") then | |
61 local rest_name = name:gsub("^[^.]+%.", "") | |
62 if host_chopped == rest_name:lower() then | |
63 log("debug", "Cert dNSName %s matched hostname", name); | |
64 return true | |
65 end | |
66 end | |
67 end | |
68 | |
69 return false | |
70 end | |
71 | |
72 -- Compare an XMPP domain name with the asserted id-on-xmppAddr | |
73 -- identities extracted from a certificate. Both are UTF8 strings. | |
74 -- | |
75 -- Per [XMPP-CORE], matches against asserted identities don't include | |
76 -- wildcards, so we just do a normalize on both and then a string comparison | |
77 -- | |
78 -- TODO: Support for full JIDs? | |
79 local function compare_xmppaddr(host, asserted_names) | |
80 local norm_host = nameprep(host) | |
81 | |
82 for i=1,#asserted_names do | |
83 local name = asserted_names[i] | |
84 | |
85 -- We only want to match against bare domains right now, not | |
86 -- those crazy full-er JIDs. | |
87 if name:match("[@/]") then | |
88 log("debug", "Ignoring xmppAddr %s because it's not a bare domain", name) | |
89 else | |
90 local norm_name = nameprep(name) | |
91 if norm_name == nil then | |
92 log("info", "Ignoring xmppAddr %s, failed nameprep!", name) | |
93 else | |
94 if norm_host == norm_name then | |
95 log("debug", "Cert xmppAddr %s matched hostname", name) | |
96 return true | |
97 end | |
98 end | |
99 end | |
100 end | |
101 | |
102 return false | |
103 end | |
104 | |
105 -- Compare a host + service against the asserted id-on-dnsSRV (SRV-ID) | |
106 -- identities extracted from a certificate. | |
107 -- | |
108 -- Per [SRV-ID], the asserted identities will be encoded in ASCII via ToASCII. | |
109 -- Comparison is done case-insensitively, and a wildcard ("*") all by itself | |
110 -- is allowed only as the left-most non-service label. | |
111 local function compare_srvname(host, service, asserted_names) | |
112 local norm_host = idna_to_ascii(host) | |
113 if norm_host == nil then | |
114 log("info", "Host %s failed IDNA ToASCII operation", host); | |
115 return false | |
116 end | |
117 | |
118 -- Service names start with a "_" | |
119 if service:match("^_") == nil then service = "_"..service end | |
120 | |
121 norm_host = norm_host:lower(); | |
122 local host_chopped = norm_host:gsub("^[^.]+%.", "") -- everything after the first label | |
123 | |
124 for i=1,#asserted_names do | |
125 local asserted_service, name = asserted_names[i]:match("^(_[^.]+)%.(.*)"); | |
126 if service == asserted_service then | |
127 if norm_host == name:lower() then | |
128 log("debug", "Cert SRVName %s matched hostname", name); | |
129 return true; | |
130 end | |
131 | |
132 -- Allow the left most label to be a "*" | |
133 if name:match("^%*%.") then | |
134 local rest_name = name:gsub("^[^.]+%.", "") | |
135 if host_chopped == rest_name:lower() then | |
136 log("debug", "Cert SRVName %s matched hostname", name) | |
137 return true | |
138 end | |
139 end | |
140 if norm_host == name:lower() then | |
141 log("debug", "Cert SRVName %s matched hostname", name); | |
142 return true | |
143 end | |
144 end | |
145 end | |
146 | |
147 return false | |
148 end | |
149 | |
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
|
150 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
|
151 if cert.setencode then |
d2beb98ece29
util.x509: Tell LuaSec we want UTF-8 data
Kim Alvefur <zash@zash.se>
parents:
6153
diff
changeset
|
152 cert:setencode("utf8"); |
d2beb98ece29
util.x509: Tell LuaSec we want UTF-8 data
Kim Alvefur <zash@zash.se>
parents:
6153
diff
changeset
|
153 end |
3651 | 154 local ext = cert:extensions() |
155 if ext[oid_subjectaltname] then | |
156 local sans = ext[oid_subjectaltname]; | |
157 | |
4330
520fcb333cba
util.x509: Update references to published RFCs
Paul Aurich <paul@darkrain42.org>
parents:
3735
diff
changeset
|
158 -- Per [TLS-CERTS] 6.3, 6.4.4, "a client MUST NOT seek a match for a |
3651 | 159 -- reference identifier if the presented identifiers include a DNS-ID |
160 -- SRV-ID, URI-ID, or any application-specific identifier types" | |
161 local had_supported_altnames = false | |
162 | |
163 if sans[oid_xmppaddr] then | |
164 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
|
165 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
|
166 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
|
167 end |
3651 | 168 end |
169 | |
170 if sans[oid_dnssrv] then | |
171 had_supported_altnames = true | |
172 -- Only check srvNames if the caller specified a service | |
173 if service and compare_srvname(host, service, sans[oid_dnssrv]) then return true end | |
174 end | |
175 | |
176 if sans["dNSName"] then | |
177 had_supported_altnames = true | |
178 if compare_dnsname(host, sans["dNSName"]) then return true end | |
179 end | |
180 | |
181 -- We don't need URIs, but [TLS-CERTS] is clear. | |
182 if sans["uniformResourceIdentifier"] then | |
183 had_supported_altnames = true | |
184 end | |
185 | |
186 if had_supported_altnames then return false end | |
187 end | |
188 | |
189 -- Extract a common name from the certificate, and check it as if it were | |
190 -- a dNSName subjectAltName (wildcards may apply for, and receive, | |
191 -- cat treats) | |
192 -- | |
4330
520fcb333cba
util.x509: Update references to published RFCs
Paul Aurich <paul@darkrain42.org>
parents:
3735
diff
changeset
|
193 -- Per [TLS-CERTS] 1.8, a CN-ID is the Common Name from a cert subject |
3651 | 194 -- which has one and only one Common Name |
195 local subject = cert:subject() | |
196 local cn = nil | |
197 for i=1,#subject do | |
198 local dn = subject[i] | |
199 if dn["oid"] == oid_commonname then | |
200 if cn then | |
201 log("info", "Certificate has multiple common names") | |
202 return false | |
203 end | |
204 | |
205 cn = dn["value"]; | |
206 end | |
207 end | |
208 | |
209 if cn then | |
4330
520fcb333cba
util.x509: Update references to published RFCs
Paul Aurich <paul@darkrain42.org>
parents:
3735
diff
changeset
|
210 -- Per [TLS-CERTS] 6.4.4, follow the comparison rules for dNSName SANs. |
3651 | 211 return compare_dnsname(host, { cn }) |
212 end | |
213 | |
214 -- If all else fails, well, why should we be any different? | |
215 return false | |
216 end | |
217 | |
6152
fbab74c28e31
util.x509: And functions for converting between DER and PEM
Kim Alvefur <zash@zash.se>
parents:
5845
diff
changeset
|
218 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
|
219 "([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
|
220 |
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
|
221 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
|
222 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
|
223 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
|
224 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
|
225 end |
fbab74c28e31
util.x509: And functions for converting between DER and PEM
Kim Alvefur <zash@zash.se>
parents:
5845
diff
changeset
|
226 end |
fbab74c28e31
util.x509: And functions for converting between DER and PEM
Kim Alvefur <zash@zash.se>
parents:
5845
diff
changeset
|
227 |
fbab74c28e31
util.x509: And functions for converting between DER and PEM
Kim Alvefur <zash@zash.se>
parents:
5845
diff
changeset
|
228 local wrap = ('.'):rep(64); |
fbab74c28e31
util.x509: And functions for converting between DER and PEM
Kim Alvefur <zash@zash.se>
parents:
5845
diff
changeset
|
229 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
|
230 |
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
|
231 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
|
232 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
|
233 data = base64.encode(data); |
fbab74c28e31
util.x509: And functions for converting between DER and PEM
Kim Alvefur <zash@zash.se>
parents:
5845
diff
changeset
|
234 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
|
235 end |
fbab74c28e31
util.x509: And functions for converting between DER and PEM
Kim Alvefur <zash@zash.se>
parents:
5845
diff
changeset
|
236 |
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
|
237 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
|
238 verify_identity = verify_identity; |
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
|
239 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
|
240 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
|
241 }; |