Merge with Zash
authorMatthew Wild <mwild1@gmail.com>
Thu, 10 May 2012 23:10:56 +0100
changeset 4833 b7a6e86ab87d
parent 4822 5ef05f32bc42 (current diff)
parent 4832 6b3aec1e0d9f (diff)
child 4834 878f75ccc4fb
Merge with Zash
core/s2smanager.lua
--- a/core/s2smanager.lua	Thu May 10 22:59:01 2012 +0100
+++ b/core/s2smanager.lua	Thu May 10 23:10:56 2012 +0100
@@ -9,40 +9,15 @@
 
 
 local hosts = hosts;
-local core_process_stanza = function(a, b) core_process_stanza(a, b); end
-local format = string.format;
-local t_insert, t_sort = table.insert, table.sort;
-local get_traceback = debug.traceback;
-local tostring, pairs, ipairs, getmetatable, newproxy, type, error, tonumber, setmetatable
-    = tostring, pairs, ipairs, getmetatable, newproxy, type, error, tonumber, setmetatable;
-
-local initialize_filters = require "util.filters".initialize;
-local wrapclient = require "net.server".wrapclient;
-local st = require "stanza";
-local stanza = st.stanza;
-local nameprep = require "util.encodings".stringprep.nameprep;
-local cert_verify_identity = require "util.x509".verify_identity;
-local new_ip = require "util.ip".new_ip;
-local rfc3484_dest = require "util.rfc3484".destination;
+local tostring, pairs, ipairs, getmetatable, newproxy, setmetatable
+    = tostring, pairs, ipairs, getmetatable, newproxy, setmetatable;
 
 local fire_event = prosody.events.fire_event;
-local uuid_gen = require "util.uuid".generate;
-
 local logger_init = require "util.logger".init;
 
 local log = logger_init("s2smanager");
 
-local sha256_hash = require "util.hashes".sha256;
-
-local adns, dns = require "net.adns", require "net.dns";
 local config = require "core.configmanager";
-local dns_timeout = config.get("*", "core", "dns_timeout") or 15;
-local cfg_sources = config.get("*", "core", "s2s_interface")
-	or config.get("*", "core", "interface");
-local sources;
-
---FIXME: s2sout should create its own resolver w/ timeout
-dns.settimeout(dns_timeout);
 
 local prosody = _G.prosody;
 incoming_s2s = {};
--- a/plugins/mod_s2s/s2sout.lib.lua	Thu May 10 22:59:01 2012 +0100
+++ b/plugins/mod_s2s/s2sout.lib.lua	Thu May 10 23:10:56 2012 +0100
@@ -26,6 +26,8 @@
 
 local sources = {};
 
+local dns_timeout = module:get_option_number("dns_timeout", 15);
+dns.settimeout(dns_timeout);
 local max_dns_depth = module:get_option_number("dns_max_depth", 3);
 
 local s2sout = {};
--- a/prosodyctl	Thu May 10 22:59:01 2012 +0100
+++ b/prosodyctl	Thu May 10 23:10:56 2012 +0100
@@ -205,6 +205,7 @@
 		["invalid-hostname"] = "The given hostname is invalid";
 		["no-password"] = "No password was supplied";
 		["no-such-user"] = "The given user does not exist on the server";
+		["no-such-host"] = "The given hostname does not exist in the config";
 		["unable-to-save-data"] = "Unable to store, perhaps you don't have permission?";
 		["no-pidfile"] = "There is no 'pidfile' option in the configuration file, see http://prosody.im/doc/prosodyctl#pidfile for help";
 		["no-posix"] = "The mod_posix module is not enabled in the Prosody config file, see http://prosody.im/doc/prosodyctl for more info";
@@ -613,23 +614,23 @@
 	return 1;
 end
 
-local x509 = require "util.x509";
-local genx509san = x509.genx509san;
-local opensslbaseconf = x509.baseconf;
-local seralizeopensslbaseconf = x509.serialize_conf;
+local openssl = require "util.openssl";
+local lfs = require "lfs";
 
 local cert_commands = {};
 
--- TODO Should this be moved to util.prosodyctl or x509?
+local function ask_overwrite(filename)
+	return lfs.attributes(filename) and not show_yesno("Overwrite "..filename .. "?");
+end
+
 function cert_commands.config(arg)
 	if #arg >= 1 and arg[1] ~= "--help" then
 		local conf_filename = (CFG_DATADIR or ".") .. "/" .. arg[1] .. ".cnf";
-		if os.execute("test -f "..conf_filename) == 0
-			and not show_yesno("Overwrite "..conf_filename .. "?") then
+		if ask_overwrite(conf_filename) then
 			return nil, conf_filename;
 		end
-		local conf = opensslbaseconf();
-		conf.subject_alternative_name = genx509san(hosts, config, arg, true)
+		local conf = openssl.config.new();
+		conf:from_prosody(hosts, config, arg);
 		for k, v in pairs(conf.distinguished_name) do
 			local nv;
 			if k == "commonName" then 
@@ -642,28 +643,30 @@
 			conf.distinguished_name[k] = nv ~= "." and nv or nil;
 		end
 		local conf_file = io.open(conf_filename, "w");
-		conf_file:write(seralizeopensslbaseconf(conf));
+		conf_file:write(conf:serialize());
 		conf_file:close();
 		print("");
 		show_message("Config written to " .. conf_filename);
 		return nil, conf_filename;
 	else
-		show_usage("cert config HOSTNAME", "generates config for OpenSSL")
+		show_usage("cert config HOSTNAME", "builds a config for OpenSSL")
 	end
 end
 
 function cert_commands.key(arg)
 	if #arg >= 1 and arg[1] ~= "--help" then
 		local key_filename = (CFG_DATADIR or ".") .. "/" .. arg[1] .. ".key";
-		if os.execute("test -f "..key_filename) == 0
-			and not show_yesno("Overwrite "..key_filename .. "?") then
+		if ask_overwrite(key_filename) then
 			return nil, key_filename;
 		end
+		os.remove(key_filename); -- We chmod this file to not have write permissions
 		local key_size = tonumber(arg[2] or show_prompt("Choose key size (2048):") or 2048);
-		os.execute(("openssl genrsa -out %s %d"):format(key_filename, tonumber(key_size)));
-		os.execute(("chmod 400 %s"):format(key_filename));
-		show_message("Key written to ".. key_filename);
-		return nil, key_filename;
+		if openssl.genrsa{out=key_filename, key_size} then
+			os.execute(("chmod 400 '%s'"):format(key_filename));
+			show_message("Key written to ".. key_filename);
+			return nil, key_filename;
+		end
+		show_message("There was a problem, see OpenSSL output");
 	else
 		show_usage("cert key HOSTNAME <bits>", "Generates a RSA key")
 	end
@@ -672,15 +675,16 @@
 function cert_commands.request(arg)
 	if #arg >= 1 and arg[1] ~= "--help" then
 		local req_filename = (CFG_DATADIR or ".") .. "/" .. arg[1] .. ".req";
-		if os.execute("test -f "..req_filename) == 0
-			and not show_yesno("Overwrite "..req_filename .. "?") then
+		if ask_overwrite(req_filename) then
 			return nil, req_filename;
 		end
 		local _, key_filename = cert_commands.key({arg[1]});
 		local _, conf_filename = cert_commands.config({arg[1]});
-		os.execute(("openssl req -new -key %s -utf8 -config %s -out %s")
-			:format(key_filename, conf_filename, req_filename));
-		show_message("Certificate request written to ".. req_filename);
+		if openssl.req{new=true, key=key_filename, utf8=true, config=conf_filename, out=req_filename} then
+			show_message("Certificate request written to ".. req_filename);
+		else
+			show_message("There was a problem, see OpenSSL output");
+		end
 	else
 		show_usage("cert request HOSTNAME", "Generates a certificate request")
 	end
@@ -689,15 +693,19 @@
 function cert_commands.generate(arg)
 	if #arg >= 1 and arg[1] ~= "--help" then
 		local cert_filename = (CFG_DATADIR or ".") .. "/" .. arg[1] .. ".cert";
-		if os.execute("test -f "..cert_filename) == 0
-			and not show_yesno("Overwrite "..cert_filename .. "?") then
-			return nil, cert_filename;
+		if ask_overwrite(cert_filename) then
+			return nil, conf_filename;
 		end
 		local _, key_filename = cert_commands.key({arg[1]});
 		local _, conf_filename = cert_commands.config({arg[1]});
-		os.execute(("openssl req -new -x509 -nodes -key %s -days 365 -sha1 -utf8 -config %s -out %s")
-			:format(key_filename, conf_filename, cert_filename));
-		show_message("Certificate written to ".. cert_filename);
+		local ret;
+		if key_filename and conf_filename and cert_filename
+			and openssl.req{new=true, x509=true, nodes=true, key=key_filename,
+				days=365, sha1=true, utf8=true, config=conf_filename, out=cert_filename} then
+			show_message("Certificate written to ".. cert_filename);
+		else
+			show_message("There was a problem, see OpenSSL output");
+		end
 	else
 		show_usage("cert generate HOSTNAME", "Generates a self-signed certificate")
 	end
@@ -707,6 +715,10 @@
 	if #arg >= 1 and arg[1] ~= "--help" then
 		local subcmd = table.remove(arg, 1);
 		if type(cert_commands[subcmd]) == "function" then
+			if not hosts[arg[1]] then
+				show_message(error_messages["no-such-host"]);
+				return
+			end
 			return cert_commands[subcmd](arg);
 		end
 	end
--- a/util-src/hashes.c	Thu May 10 22:59:01 2012 +0100
+++ b/util-src/hashes.c	Thu May 10 23:10:56 2012 +0100
@@ -46,14 +46,20 @@
 	return 1; \
 }
 
-MAKE_HASH_FUNCTION(Lsha1, SHA1, 20)
-MAKE_HASH_FUNCTION(Lsha256, SHA256, 32)
-MAKE_HASH_FUNCTION(Lmd5, MD5, 16)
+MAKE_HASH_FUNCTION(Lsha1, SHA1, SHA_DIGEST_LENGTH)
+MAKE_HASH_FUNCTION(Lsha224, SHA224, SHA224_DIGEST_LENGTH)
+MAKE_HASH_FUNCTION(Lsha256, SHA256, SHA256_DIGEST_LENGTH)
+MAKE_HASH_FUNCTION(Lsha384, SHA384, SHA384_DIGEST_LENGTH)
+MAKE_HASH_FUNCTION(Lsha512, SHA512, SHA512_DIGEST_LENGTH)
+MAKE_HASH_FUNCTION(Lmd5, MD5, MD5_DIGEST_LENGTH)
 
 static const luaL_Reg Reg[] =
 {
 	{ "sha1",	Lsha1	},
+	{ "sha224",	Lsha224	},
 	{ "sha256",	Lsha256	},
+	{ "sha384",	Lsha384	},
+	{ "sha512",	Lsha512	},
 	{ "md5",	Lmd5	},
 	{ NULL,		NULL	}
 };
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/util/openssl.lua	Thu May 10 23:10:56 2012 +0100
@@ -0,0 +1,156 @@
+local type, tostring, pairs, ipairs = type, tostring, pairs, ipairs;
+local t_insert, t_concat = table.insert, table.concat;
+local s_format = string.format;
+
+local oid_xmppaddr = "1.3.6.1.5.5.7.8.5"; -- [XMPP-CORE]
+local oid_dnssrv   = "1.3.6.1.5.5.7.8.7"; -- [SRV-ID]
+
+local idna_to_ascii = require "util.encodings".idna.to_ascii;
+
+local _M = {};
+local config = {};
+_M.config = config;
+
+local ssl_config = {};
+local ssl_config_mt = {__index=ssl_config};
+
+function config.new()
+	return setmetatable({
+		req = {
+			distinguished_name = "distinguished_name",
+			req_extensions = "v3_extensions",
+			x509_extensions = "v3_extensions",
+			prompt = "no",
+		},
+		distinguished_name = {
+			commonName = "example.com",
+			countryName = "GB",
+			localityName = "The Internet",
+			organizationName = "Your Organisation",
+			organizationalUnitName = "XMPP Department",
+			emailAddress = "xmpp@example.com",
+		},
+		v3_extensions = {
+			basicConstraints = "CA:FALSE",
+			keyUsage = "digitalSignature,keyEncipherment",
+			extendedKeyUsage = "serverAuth,clientAuth",
+			subjectAltName = "@subject_alternative_name",
+		},
+		subject_alternative_name = {
+			DNS = {},
+			otherName = {},
+		},
+	}, ssl_config_mt);
+end
+
+function ssl_config:serialize()
+	local s = "";
+	for k, t in pairs(self) do
+		s = s .. ("[%s]\n"):format(k);
+		if k == "subject_alternative_name" then
+			for san, n in pairs(t) do
+				for i = 1,#n do
+					s = s .. s_format("%s.%d = %s\n", san, i -1, n[i]);
+				end
+			end
+		else
+			for k, v in pairs(t) do
+				s = s .. ("%s = %s\n"):format(k, v);
+			end
+		end
+		s = s .. "\n";
+	end
+	return s;
+end
+
+local function utf8string(s)
+	-- This is how we tell openssl not to encode UTF-8 strings as fake Latin1
+	return s_format("FORMAT:UTF8,UTF8:%s", s);
+end
+
+local function ia5string(s)
+	return s_format("IA5STRING:%s", s);
+end
+
+local util = {};
+_M.util = {
+	utf8string = utf8string,
+	ia5string = ia5string,
+};
+
+local function xmppAddr(t, host)
+end
+
+function ssl_config:add_dNSName(host)
+	t_insert(self.subject_alternative_name.DNS, idna_to_ascii(host));
+end
+
+function ssl_config:add_sRVName(host, service)
+	t_insert(self.subject_alternative_name.otherName,
+		s_format("%s;%s", oid_dnssrv, ia5string("_" .. service .."." .. idna_to_ascii(host))));
+end
+
+function ssl_config:add_xmppAddr(host)
+	t_insert(self.subject_alternative_name.otherName,
+		s_format("%s;%s", oid_xmppaddr, utf8string(host)));
+end
+
+function ssl_config:from_prosody(hosts, config, certhosts, raw)
+	-- TODO Decide if this should go elsewhere
+	local found_matching_hosts = false;
+	for i = 1,#certhosts do
+		local certhost = certhosts[i];
+		for name, host in pairs(hosts) do
+			if name == certhost or name:sub(-1-#certhost) == "."..certhost then
+				found_matching_hosts = true;
+				self:add_dNSName(name);
+				--print(name .. "#component_module: " .. (config.get(name, "core", "component_module") or "nil"));
+				if config.get(name, "core", "component_module") == nil then
+					self:add_sRVName(name, "xmpp-client");
+				end
+				--print(name .. "#anonymous_login: " .. tostring(config.get(name, "core", "anonymous_login")));
+				if not (config.get(name, "core", "anonymous_login") or
+						config.get(name, "core", "authentication") == "anonymous") then
+					self:add_sRVName(name, "xmpp-server");
+				end
+				self:add_xmppAddr(name);
+			end
+		end
+	end
+	if not found_matching_hosts then
+		return nil, "no-matching-hosts";
+	end
+end
+
+do -- Lua to shell calls.
+	local function shell_escape(s)
+		return s:gsub("'",[['\'']]);
+	end
+
+	local function serialize(f,o)
+		local r = {"openssl", f};
+		for k,v in pairs(o) do
+			if type(k) == "string" then
+				t_insert(r, ("-%s"):format(k));
+				if v ~= true then
+					t_insert(r, ("'%s'"):format(shell_escape(tostring(v))));
+				end
+			end
+		end
+		for k,v in ipairs(o) do
+			t_insert(r, ("'%s'"):format(shell_escape(tostring(v))));
+		end
+		return t_concat(r, " ");
+	end
+
+	local os_execute = os.execute;
+	setmetatable(_M, {
+		__index=function(self,f)
+			return function(opts)
+				return 0 == os_execute(serialize(f, type(opts) == "table" and opts or {}));
+			end;
+		end;
+	});
+end
+
+return _M;
--- a/util/rfc3484.lua	Thu May 10 22:59:01 2012 +0100
+++ b/util/rfc3484.lua	Thu May 10 23:10:56 2012 +0100
@@ -19,7 +19,7 @@
 	end
 end
 
-function source(dest, candidates)
+local function source(dest, candidates)
 	local function comp(ipA, ipB)
 		-- Rule 1: Prefer same address
 		if dest == ipA then
@@ -70,7 +70,7 @@
 	return candidates[1];
 end
 
-function destination(candidates, sources)
+local function destination(candidates, sources)
 	local sourceAddrs = {};
 	local function comp(ipA, ipB)
 		local ipAsource = sourceAddrs[ipA];
--- a/util/x509.lua	Thu May 10 22:59:01 2012 +0100
+++ b/util/x509.lua	Thu May 10 23:10:56 2012 +0100
@@ -212,109 +212,4 @@
 	return false
 end
 
--- TODO Rename? Split out subroutines?
--- Also, this is probably openssl specific, what TODO about that?
-function genx509san(hosts, config, certhosts, raw) -- recive config through that or some better way?
-	local function utf8string(s)
-		-- This is how we tell openssl not to encode UTF-8 strings as Latin1
-		return s_format("FORMAT:UTF8,UTF8:%s", s);
-	end
-
-	local function ia5string(s)
-		return s_format("IA5STRING:%s", s);
-	end
-
-	local function dnsname(t, host)
-		t_insert(t.DNS, idna_to_ascii(host));
-	end
-
-	local function srvname(t, host, service)
-		t_insert(t.otherName, s_format("%s;%s", oid_dnssrv, ia5string("_" .. service .."." .. idna_to_ascii(host))));
-	end
-
-	local function xmppAddr(t, host)
-		t_insert(t.otherName, s_format("%s;%s", oid_xmppaddr, utf8string(host)));
-	end
-
-	-----------------------------
-
-	local san = {
-		DNS = {};
-		otherName = {};
-	};
-
-	local sslsanconf = { };
-
-	for i = 1,#certhosts do
-		local certhost = certhosts[i];
-		for name, host in pairs(hosts) do
-			if name == certhost or name:sub(-1-#certhost) == "."..certhost then
-				dnsname(san, name);
-				--print(name .. "#component_module: " .. (config.get(name, "core", "component_module") or "nil"));
-				if config.get(name, "core", "component_module") == nil then
-					srvname(san, name, "xmpp-client");
-				end
-				--print(name .. "#anonymous_login: " .. tostring(config.get(name, "core", "anonymous_login")));
-				if not (config.get(name, "core", "anonymous_login") or
-						config.get(name, "core", "authentication") == "anonymous") then
-					srvname(san, name, "xmpp-server");
-				end
-				xmppAddr(san, name);
-			end
-		end
-	end
-
-	for t, n in pairs(san) do
-		for i = 1,#n do
-			t_insert(sslsanconf, s_format("%s.%d = %s", t, i -1, n[i]));
-		end
-	end
-
-	return raw and sslsanconf or t_concat(sslsanconf, "\n");
-end
-
-function baseconf()
-	return {
-		req = {
-			distinguished_name = "distinguished_name",
-			req_extensions = "v3_extensions",
-			x509_extensions = "v3_extensions",
-			prompt = "no",
-		},
-		distinguished_name = {
-			commonName = "example.com",
-			countryName = "GB",
-			localityName = "The Internet",
-			organizationName = "Your Organisation",
-			organizationalUnitName = "XMPP Department",
-			emailAddress = "xmpp@example.com",
-		},
-		v3_extensions = {
-			basicConstraints = "CA:FALSE",
-			keyUsage = "digitalSignature,keyEncipherment",
-			extendedKeyUsage = "serverAuth,clientAuth",
-			subjectAltName = "@subject_alternative_name",
-		},
-		subject_alternative_name = { },
-	}
-end
-
-function serialize_conf(conf)
-	local s = "";
-	for k, t in pairs(conf) do
-		s = s .. ("[%s]\n"):format(k);
-		if t[1] then
-			for i, v in ipairs(t) do
-				s = s .. ("%s\n"):format(v);
-			end
-		else
-			for k, v in pairs(t) do
-				s = s .. ("%s = %s\n"):format(k, v);
-			end
-		end
-		s = s .. "\n";
-	end
-	return s;
-end
-
 return _M;