Merge 0.9->0.10
[prosody.git] / util / x509.lua
index 11f231a04b12f93882f9a138cd3b16423a5ca473..f228b20130316e21e76f96b4e44d62dc9d2c0ebc 100644 (file)
@@ -11,8 +11,8 @@
 -- IDN libraries complicate that.
 
 
--- [TLS-CERTS] - http://tools.ietf.org/html/draft-saintandre-tls-server-id-check-10
--- [XMPP-CORE] - http://tools.ietf.org/html/draft-ietf-xmpp-3920bis-18
+-- [TLS-CERTS] - http://tools.ietf.org/html/rfc6125
+-- [XMPP-CORE] - http://tools.ietf.org/html/rfc6120
 -- [SRV-ID]    - http://tools.ietf.org/html/rfc4985
 -- [IDNA]      - http://tools.ietf.org/html/rfc5890
 -- [LDAP]      - http://tools.ietf.org/html/rfc4519
 
 local nameprep = require "util.encodings".stringprep.nameprep;
 local idna_to_ascii = require "util.encodings".idna.to_ascii;
+local base64 = require "util.encodings".base64;
 local log = require "util.logger".init("x509");
+local s_format = string.format;
 
-module "x509"
+local _ENV = nil;
 
 local oid_commonname = "2.5.4.3"; -- [LDAP] 2.3
 local oid_subjectaltname = "2.5.29.17"; -- [PKIX] 4.2.1.6
@@ -32,7 +34,7 @@ local oid_dnssrv   = "1.3.6.1.5.5.7.8.7"; -- [SRV-ID]
 -- Compare a hostname (possibly international) with asserted names
 -- extracted from a certificate.
 -- This function follows the rules laid out in
--- sections 4.4.1 and 4.4.2 of [TLS-CERTS]
+-- sections 6.4.1 and 6.4.2 of [TLS-CERTS]
 --
 -- A wildcard ("*") all by itself is allowed only as the left-most label
 local function compare_dnsname(host, asserted_names)
@@ -145,19 +147,24 @@ local function compare_srvname(host, service, asserted_names)
        return false
 end
 
-function verify_identity(host, service, cert)
+local function verify_identity(host, service, cert)
+       if cert.setencode then
+               cert:setencode("utf8");
+       end
        local ext = cert:extensions()
        if ext[oid_subjectaltname] then
                local sans = ext[oid_subjectaltname];
 
-               -- Per [TLS-CERTS] 4.3, 4.4.4, "a client MUST NOT seek a match for a
+               -- Per [TLS-CERTS] 6.3, 6.4.4, "a client MUST NOT seek a match for a
                -- reference identifier if the presented identifiers include a DNS-ID
                -- SRV-ID, URI-ID, or any application-specific identifier types"
                local had_supported_altnames = false
 
                if sans[oid_xmppaddr] then
                        had_supported_altnames = true
-                       if compare_xmppaddr(host, sans[oid_xmppaddr]) then return true end
+                       if service == "_xmpp-client" or service == "_xmpp-server" then
+                               if compare_xmppaddr(host, sans[oid_xmppaddr]) then return true end
+                       end
                end
 
                if sans[oid_dnssrv] then
@@ -183,7 +190,7 @@ function verify_identity(host, service, cert)
        -- a dNSName subjectAltName (wildcards may apply for, and receive,
        -- cat treats)
        --
-       -- Per [TLS-CERTS] 1.5, a CN-ID is the Common Name from a cert subject
+       -- Per [TLS-CERTS] 1.8, a CN-ID is the Common Name from a cert subject
        -- which has one and only one Common Name
        local subject = cert:subject()
        local cn = nil
@@ -200,7 +207,7 @@ function verify_identity(host, service, cert)
        end
 
        if cn then
-               -- Per [TLS-CERTS] 4.4.4, follow the comparison rules for dNSName SANs.
+               -- Per [TLS-CERTS] 6.4.4, follow the comparison rules for dNSName SANs.
                return compare_dnsname(host, { cn })
        end
 
@@ -208,4 +215,27 @@ function verify_identity(host, service, cert)
        return false
 end
 
-return _M;
+local pat = "%-%-%-%-%-BEGIN ([A-Z ]+)%-%-%-%-%-\r?\n"..
+"([0-9A-Za-z+/=\r\n]*)\r?\n%-%-%-%-%-END %1%-%-%-%-%-";
+
+local function pem2der(pem)
+       local typ, data = pem:match(pat);
+       if typ and data then
+               return base64.decode(data), typ;
+       end
+end
+
+local wrap = ('.'):rep(64);
+local envelope = "-----BEGIN %s-----\n%s\n-----END %s-----\n"
+
+local function der2pem(data, typ)
+       typ = typ and typ:upper() or "CERTIFICATE";
+       data = base64.encode(data);
+       return s_format(envelope, typ, data:gsub(wrap, '%0\n', (#data-1)/64), typ);
+end
+
+return {
+       verify_identity = verify_identity;
+       pem2der = pem2der;
+       der2pem = der2pem;
+};