Add ssl.checkhostname and conn:checkhostname

Convenient (and important!) methods that allow checking for hostnames
against a (peer) certificate. Deals with wildcards and alternative names
(via the dnsnames extension).
This commit is contained in:
Bart van Strien 2015-06-05 15:54:50 +02:00
parent 88fa9b8bc2
commit cde151739e
3 changed files with 65 additions and 0 deletions

View File

@ -47,3 +47,13 @@ Alias for `cert.load`.
`ssl.wrap` wraps an existing luasocket socket into a luasec connection object.
`cfg` is defined as for `ssl.newcontext`.
### ssl.checkhostname ###
valid = ssl.checkhostname(cert, hostname)
Check if the certificate is valid for the given hostname. Deals with wildcards
and alternative names.
**NOTE**: It is crucial the hostname is checked to verify the certificate is
not only valid, but belonging to the host connected to.

View File

@ -119,3 +119,10 @@ context associated with this `conn` object.
Returns luasec's current `want`, if the connection is dirty (see `conn:dirty`).
This can either be `nothing`, `read`, `write` or `x509lookup`.
### conn:checkhostname ###
valid = conn:checkhostname(hostname)
Checks whether the certificate is valid for the given hostname. Helper for
`ssl.checkhostname`, see `ssl.checkhostname` for more information.

View File

@ -159,10 +159,57 @@ local function info(ssl, field)
return ( (next(info)) and info )
end
--
-- Verify host name against a common name
--
local function checkhostname_single(hostname, cn)
if cn:match("^%*%.") then
-- If it's a wildcard domain name, strip the first element of the hostname
-- and the cn, then check neither are empty.
hostname = hostname:match("%.(.+)$")
cn = cn:match("%.(.+)$")
if cn == "" or hostname == "" then return false end
end
return cn == hostname
end
--
-- Verify host name against certificate
--
local function checkhostname(cert, hostname)
local subject, ext
subject = cert:subject()
for i, v in ipairs(subject) do
if v.name == "commonName" then
if checkhostname_single(hostname, v.value) then
return true
end
break
end
end
-- If we got here, the cn doesn't match, check for the dNSName extension
ext = (cert:extensions() or {})["2.5.29.17"]
if not ext or not ext.dNSName then return false end
for i, v in ipairs(ext.dNSName) do
if checkhostname_single(hostname, v) then
return true
end
end
return false
end
--
-- Verify host name against peer certificate
--
local function checkhostname_ssl(ssl, hostname)
return checkhostname(ssl:getpeercertificate(), hostname)
end
--
-- Set method for SSL connections.
--
core.setmethod("info", info)
core.setmethod("checkhostname", checkhostname_ssl)
--------------------------------------------------------------------------------
-- Export module
@ -174,6 +221,7 @@ local _M = {
loadcertificate = x509.load,
newcontext = newcontext,
wrap = wrap,
checkhostname = checkhostname,
}
return _M