Changeset

4488:99b37efe13ed

Merge with Zash
author Matthew Wild <mwild1@gmail.com>
date Thu, 26 Jan 2012 18:47:59 +0000
parents 4484:0da4e0f0f0ef (current diff) 4487:5f466a50e78b (diff)
children 4494:694491140a67
files
diffstat 3 files changed, 225 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/prosodyctl	Wed Jan 25 11:54:12 2012 +0500
+++ b/prosodyctl	Thu Jan 26 18:47:59 2012 +0000
@@ -236,6 +236,7 @@
 local show_usage = prosodyctl.show_usage;
 local getchar, getpass = prosodyctl.getchar, prosodyctl.getpass;
 local show_yesno = prosodyctl.show_yesno;
+local show_prompt = prosodyctl.show_prompt;
 local read_password = prosodyctl.read_password;
 
 local prosodyctl_timeout = (config.get("*", "core", "prosodyctl_timeout") or 5) * 2;
@@ -612,6 +613,106 @@
 	return 1;
 end
 
+local x509 = require "util.x509";
+local genx509san = x509.genx509san;
+local opensslbaseconf = x509.baseconf;
+local seralizeopensslbaseconf = x509.serialize_conf;
+
+local cert_commands = {};
+
+-- TODO Should this be moved to util.prosodyctl or x509?
+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
+			return nil, conf_filename;
+		end
+		local conf = opensslbaseconf();
+		conf.subject_alternative_name = genx509san(hosts, config, arg, true)
+		for k, v in pairs(conf.distinguished_name) do
+			local nv;
+			if k == "commonName" then 
+				v = arg[1]
+			elseif k == "emailAddress" then
+				v = "xmpp@" .. arg[1];
+			end
+			nv = show_prompt(("%s (%s):"):format(k, nv or v));
+			nv = (not nv or nv == "") and v or nv;
+			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:close();
+		print("");
+		show_message("Config written to " .. conf_filename);
+		return nil, conf_filename;
+	else
+		show_usage("cert config HOSTNAME", "generates 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
+			return nil, key_filename;
+		end
+		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;
+	else
+		show_usage("cert key HOSTNAME <bits>", "Generates a RSA key")
+	end
+end
+
+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
+			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);
+	else
+		show_usage("cert request HOSTNAME", "Generates a certificate request")
+	end
+end
+
+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;
+		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);
+	else
+		show_usage("cert generate HOSTNAME", "Generates a self-signed certificate")
+	end
+end
+
+function commands.cert(arg)
+	if #arg >= 1 and arg[1] ~= "--help" then
+		local subcmd = table.remove(arg, 1);
+		if type(cert_commands[subcmd]) == "function" then
+			return cert_commands[subcmd](arg);
+		end
+	end
+	show_usage("cert config|request|generate|key", "Helpers for X.509 certificates.")
+end
+
 ---------------------
 
 if command and command:match("^mod_") then -- Is a command in a module
--- a/util/prosodyctl.lua	Wed Jan 25 11:54:12 2012 +0500
+++ b/util/prosodyctl.lua	Thu Jan 26 18:47:59 2012 +0000
@@ -16,6 +16,7 @@
 local set = require "util.set";
 local lfs = require "lfs";
 local pcall = pcall;
+local type = type;
 
 local nodeprep, nameprep = stringprep.nodeprep, stringprep.nameprep;
 
@@ -63,6 +64,13 @@
 	end
 end
 
+function getline()
+	local ok, line = pcall(io.read, "*l");
+	if ok then
+		return line;
+	end
+end
+
 function getpass()
 	local stty_ret = os.execute("stty -echo 2>/dev/null");
 	if stty_ret ~= 0 then
@@ -112,6 +120,13 @@
 	return password;
 end
 
+function show_prompt(prompt)
+	io.write(prompt, " ");
+	local line = getline();
+	line = line and line:gsub("\n$","");
+	return (line and #line > 0) and line or nil;
+end
+
 -- Server control
 function adduser(params)
 	local user, host, password = nodeprep(params.user), nameprep(params.host), params.password;
--- a/util/x509.lua	Wed Jan 25 11:54:12 2012 +0500
+++ b/util/x509.lua	Thu Jan 26 18:47:59 2012 +0000
@@ -21,6 +21,10 @@
 local nameprep = require "util.encodings".stringprep.nameprep;
 local idna_to_ascii = require "util.encodings".idna.to_ascii;
 local log = require "util.logger".init("x509");
+local pairs, ipairs = pairs, ipairs;
+local s_format = string.format;
+local t_insert = table.insert;
+local t_concat = table.concat;
 
 module "x509"
 
@@ -208,4 +212,109 @@
 	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;