# HG changeset patch # User Matthew Wild # Date 1290978213 0 # Node ID 4b56cd1302d47257c73452a33dfab0e7bd9385f0 # Parent 29a340777d7bb1ab44d64559bc4b417567b87411 mod_console: Add s2s:showcert() command to show the certificate for a domain diff -r 29a340777d7b -r 4b56cd1302d4 plugins/mod_console.lua --- a/plugins/mod_console.lua Sun Nov 28 15:02:56 2010 +0500 +++ b/plugins/mod_console.lua Sun Nov 28 21:03:33 2010 +0000 @@ -561,6 +561,109 @@ return true, "Total: "..count_out.." outgoing, "..count_in.." incoming connections"; end +local function print_subject(print, subject) + for _, entry in ipairs(subject) do + print( + (" %s: %q"):format( + entry.name or entry.oid, + entry.value:gsub("[\r\n%z%c]", " ") + ) + ); + end +end + +function def_env.s2s:showcert(domain) + local ser = require "util.serialization".serialize; + local print = self.session.print; + local domain_sessions = set.new(array.collect(keys(incoming_s2s))) + /function(session) return session.from_host == domain; end; + for local_host in values(prosody.hosts) do + local s2sout = local_host.s2sout; + if s2sout and s2sout[domain] then + domain_sessions:add(s2sout[domain]); + end + end + local cert_set = {}; + for session in domain_sessions do + local conn = session.conn; + conn = conn and conn:socket(); + if not conn.getpeercertificate then + if conn.dohandshake then + error("This version of LuaSec does not support certificate viewing"); + end + else + local cert = conn:getpeercertificate(); + if cert then + local digest = cert:digest("sha1"); + if not cert_set[digest] then + local chain_valid, chain_err = conn:getpeerchainvalid(); + cert_set[digest] = { + { + from = session.from_host, + to = session.to_host, + direction = session.direction + }; + chain_valid = chain_valid; + chain_err = chain_err; + cert = cert; + }; + else + table.insert(cert_set[digest], { + from = session.from_host, + to = session.to_host, + direction = session.direction + }); + end + end + end + end + local domain_certs = array.collect(values(cert_set)); + -- Phew. We now have a array of unique certificates presented by domain. + local print = self.session.print; + local n_certs = #domain_certs; + + if n_certs == 0 then + return "No certificates found for "..domain; + end + + local function _capitalize_and_colon(byte) + return string.upper(byte)..":"; + end + local function pretty_fingerprint(hash) + return hash:gsub("..", _capitalize_and_colon):sub(1, -2); + end + + for cert_info in values(domain_certs) do + local cert = cert_info.cert; + print("---") + print("Fingerprint (SHA1): "..pretty_fingerprint(cert:digest("sha1"))); + print(""); + local n_streams = #cert_info; + print("Currently used on "..n_streams.." stream"..(n_streams==1 and "" or "s")..":"); + for _, stream in ipairs(cert_info) do + if stream.direction == "incoming" then + print(" "..stream.to.." <- "..stream.from); + else + print(" "..stream.from.." -> "..stream.to); + end + end + print(""); + local chain_valid, err = cert_info.chain_valid, cert_info.chain_err; + local valid_identity = cert_verify_identity(domain, "xmpp-server", cert); + print("Trusted certificate: "..(chain_valid and "Yes" or ("No ("..err..")"))); + print("Issuer: "); + print_subject(print, cert:issuer()); + print(""); + print("Valid for "..domain..": "..(valid_identity and "Yes" or "No")); + print("Subject:"); + print_subject(print, cert:subject()); + end + print("---"); + return ("Showing "..n_certs.." certificate" + ..(n_certs==1 and "" or "s") + .." presented by "..domain.."."); +end + function def_env.s2s:close(from, to) local print, count = self.session.print, 0;