Comparison

util/x509.lua @ 13729:b50eadfddd57 13.0

util.x509: Per RFC 9525, remove obsolete Common Name check
author Kim Alvefur <zash@zash.se>
date Sun, 11 Feb 2024 13:34:13 +0100
parent 12975:d10957394a3c
comparison
equal deleted inserted replaced
13727:704765bfe0a3 13729:b50eadfddd57
9 -- TODO: I feel a fair amount of this logic should be integrated into Luasec, 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 10 -- so that everyone isn't re-inventing the wheel. Dependencies on
11 -- IDN libraries complicate that. 11 -- IDN libraries complicate that.
12 12
13 13
14 -- [TLS-CERTS] - https://www.rfc-editor.org/rfc/rfc6125.html 14 -- [TLS-CERTS] - https://www.rfc-editor.org/rfc/rfc6125.html -- Obsolete
15 -- [TLS-IDENT] - https://www.rfc-editor.org/rfc/rfc9525.html
15 -- [XMPP-CORE] - https://www.rfc-editor.org/rfc/rfc6120.html 16 -- [XMPP-CORE] - https://www.rfc-editor.org/rfc/rfc6120.html
16 -- [SRV-ID] - https://www.rfc-editor.org/rfc/rfc4985.html 17 -- [SRV-ID] - https://www.rfc-editor.org/rfc/rfc4985.html
17 -- [IDNA] - https://www.rfc-editor.org/rfc/rfc5890.html 18 -- [IDNA] - https://www.rfc-editor.org/rfc/rfc5890.html
18 -- [LDAP] - https://www.rfc-editor.org/rfc/rfc4519.html 19 -- [LDAP] - https://www.rfc-editor.org/rfc/rfc4519.html
19 -- [PKIX] - https://www.rfc-editor.org/rfc/rfc5280.html 20 -- [PKIX] - https://www.rfc-editor.org/rfc/rfc5280.html
33 local oid_commonname = "2.5.4.3"; -- [LDAP] 2.3 34 local oid_commonname = "2.5.4.3"; -- [LDAP] 2.3
34 local oid_subjectaltname = "2.5.29.17"; -- [PKIX] 4.2.1.6 35 local oid_subjectaltname = "2.5.29.17"; -- [PKIX] 4.2.1.6
35 local oid_xmppaddr = "1.3.6.1.5.5.7.8.5"; -- [XMPP-CORE] 36 local oid_xmppaddr = "1.3.6.1.5.5.7.8.5"; -- [XMPP-CORE]
36 local oid_dnssrv = "1.3.6.1.5.5.7.8.7"; -- [SRV-ID] 37 local oid_dnssrv = "1.3.6.1.5.5.7.8.7"; -- [SRV-ID]
37 38
38 -- Compare a hostname (possibly international) with asserted names 39 -- Compare a hostname (possibly international) with asserted names extracted from a certificate.
39 -- extracted from a certificate. 40 -- This function follows the rules laid out in section 6.3 of [TLS-IDENT]
40 -- This function follows the rules laid out in
41 -- sections 6.4.1 and 6.4.2 of [TLS-CERTS]
42 -- 41 --
43 -- A wildcard ("*") all by itself is allowed only as the left-most label 42 -- A wildcard ("*") all by itself is allowed only as the left-most label
44 local function compare_dnsname(host, asserted_names) 43 local function compare_dnsname(host, asserted_names)
45 -- TODO: Sufficient normalization? Review relevant specs. 44 -- TODO: Sufficient normalization? Review relevant specs.
46 local norm_host = idna_to_ascii(host) 45 local norm_host = idna_to_ascii(host)
157 end 156 end
158 local ext = cert:extensions() 157 local ext = cert:extensions()
159 if ext[oid_subjectaltname] then 158 if ext[oid_subjectaltname] then
160 local sans = ext[oid_subjectaltname]; 159 local sans = ext[oid_subjectaltname];
161 160
162 -- Per [TLS-CERTS] 6.3, 6.4.4, "a client MUST NOT seek a match for a
163 -- reference identifier if the presented identifiers include a DNS-ID
164 -- SRV-ID, URI-ID, or any application-specific identifier types"
165 local had_supported_altnames = false
166
167 if sans[oid_xmppaddr] then 161 if sans[oid_xmppaddr] then
168 had_supported_altnames = true
169 if service == "_xmpp-client" or service == "_xmpp-server" then 162 if service == "_xmpp-client" or service == "_xmpp-server" then
170 if compare_xmppaddr(host, sans[oid_xmppaddr]) then return true end 163 if compare_xmppaddr(host, sans[oid_xmppaddr]) then return true end
171 end 164 end
172 end 165 end
173 166
174 if sans[oid_dnssrv] then 167 if sans[oid_dnssrv] then
175 had_supported_altnames = true
176 -- Only check srvNames if the caller specified a service 168 -- Only check srvNames if the caller specified a service
177 if service and compare_srvname(host, service, sans[oid_dnssrv]) then return true end 169 if service and compare_srvname(host, service, sans[oid_dnssrv]) then return true end
178 end 170 end
179 171
180 if sans["dNSName"] then 172 if sans["dNSName"] then
181 had_supported_altnames = true
182 if compare_dnsname(host, sans["dNSName"]) then return true end 173 if compare_dnsname(host, sans["dNSName"]) then return true end
183 end 174 end
184 175 end
185 -- We don't need URIs, but [TLS-CERTS] is clear. 176
186 if sans["uniformResourceIdentifier"] then 177 -- Per [TLS-IDENT] ignore the Common Name
187 had_supported_altnames = true 178 -- The server identity can only be expressed in the subjectAltNames extension;
188 end 179 -- it is no longer valid to use the commonName RDN, known as CN-ID in [TLS-CERTS].
189
190 if had_supported_altnames then return false end
191 end
192
193 -- Extract a common name from the certificate, and check it as if it were
194 -- a dNSName subjectAltName (wildcards may apply for, and receive,
195 -- cat treats)
196 --
197 -- Per [TLS-CERTS] 1.8, a CN-ID is the Common Name from a cert subject
198 -- which has one and only one Common Name
199 local subject = cert:subject()
200 local cn = nil
201 for i=1,#subject do
202 local dn = subject[i]
203 if dn["oid"] == oid_commonname then
204 if cn then
205 log("info", "Certificate has multiple common names")
206 return false
207 end
208
209 cn = dn["value"];
210 end
211 end
212
213 if cn then
214 -- Per [TLS-CERTS] 6.4.4, follow the comparison rules for dNSName SANs.
215 return compare_dnsname(host, { cn })
216 end
217 180
218 -- If all else fails, well, why should we be any different? 181 -- If all else fails, well, why should we be any different?
219 return false 182 return false
220 end 183 end
221 184