poke.lua 31 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004
  1. -- Configuration handling
  2. local short_opts = { v = "verbose", m = "mode", d = "delay" }
  3. local opts = {
  4. mode = "client",
  5. verbose = false,
  6. delay = "2",
  7. capath = "/etc/ssl/certs",
  8. cafile = nil,
  9. key = nil,
  10. certificate = nil,
  11. blacklist = "/usr/share/openssl-blacklist/",
  12. version_jid = "poke@xnyhps.nl",
  13. version_password = nil,
  14. db_password = nil,
  15. db_host = "localhost",
  16. db_port = 5432
  17. };
  18. for _, opt in ipairs(arg) do
  19. if opt:match("^%-") then
  20. local name = opt:match("^%-%-?([^%s=]+)()")
  21. name = (short_opts[name] or name):gsub("%-+", "_");
  22. opts[name] = opt:match("=(.*)$") or true;
  23. else
  24. host = opt;
  25. end
  26. end
  27. local sleep_for = tonumber(opts.delay);
  28. local mode = opts.mode;
  29. local cafile = opts.cafile;
  30. local capath = opts.capath;
  31. local key = opts.key;
  32. local certificate = opts.certificate;
  33. local openssl_blacklists = opts.blacklist;
  34. local version_jid = opts.version_jid;
  35. local version_password = opts.version_password;
  36. local db_password = opts.db_password;
  37. local db_host = opts.db_host;
  38. local db_port = opts.db_port;
  39. if not host or (mode ~= "server" and mode ~= "client") then
  40. print(string.format("Usage: %s [-v] [--mode=(server|client)] [--delay=seconds] [--capath=path] [--cafile=file] [--key=privatekey] [--certificate=certificate] [--blacklist=path] hostname", arg[0]));
  41. os.exit();
  42. end
  43. local jid = host;
  44. -- Imports
  45. local date = require("3rdparty.date");
  46. local ssl = require("ssl");
  47. local io = require("io");
  48. local os = require("os");
  49. local ciphertable = require("ciphertable");
  50. local adns = require("net.adns");
  51. local sql = require("sql");
  52. local certmanager = require("certs")(openssl_blacklists);
  53. local onions = require "onions";
  54. local dbi = require('DBI');
  55. local cert_verify_identity = require "util.x509".verify_identity;
  56. local cert_load = require "ssl".x509.load;
  57. local b64 = require "util.encodings".base64.encode
  58. local verse = require("verse");
  59. local to_ascii = require "util.encodings".idna.to_ascii;
  60. local sha512 = require("util.hashes").sha512;
  61. local sha256 = require("util.hashes").sha256;
  62. local log = require("util.logger").init("poke");
  63. if opts.verbose then
  64. verse.set_log_handler(function(part, level, str) io.stdout:write(part .. " " .. level .. "\t\t" .. str .. "\n") end);
  65. end
  66. log("debug", "Connecting to database on " .. db_host .. ":" .. db_port .. ".")
  67. local dbh = assert(dbi.Connect("PostgreSQL", "xmppoke", "xmppoke", db_password, db_host, db_port));
  68. local stm = assert(dbh:prepare("SET TIMEZONE = 'UTC';"));
  69. assert(stm:execute());
  70. dbh:autocommit(false);
  71. local result_id = sql.execute_and_get_id(dbh, "INSERT INTO test_results (server_name, test_date, type) VALUES (?, 'now', ?)", host, mode)
  72. local total_score = 0;
  73. local public_key_score = 0;
  74. local fail_untrusted = false;
  75. local fail_ssl2 = false;
  76. local fail_1024 = false;
  77. local fail_md5 = false;
  78. local cap_dh_2048 = false;
  79. local cap_ssl3 = false;
  80. local cap_2048 = false;
  81. local cap_compression = false;
  82. local warn_rc4_tls11 = false;
  83. local warn_no_fs = true;
  84. local sasl_done = false;
  85. local sasl_tls_done = false;
  86. local cert_done = false;
  87. local function deep_copy(orig)
  88. local orig_type = type(orig);
  89. local copy;
  90. if orig_type == 'table' then
  91. copy = {};
  92. for orig_key, orig_value in next, orig, nil do
  93. copy[deep_copy(orig_key)] = deep_copy(orig_value);
  94. end
  95. setmetatable(copy, deep_copy(getmetatable(orig)));
  96. else
  97. copy = orig;
  98. end
  99. return copy;
  100. end
  101. local function keysize_score(pem, keytype, bits)
  102. if keytype == "RSA" or keytype == "DSA" then
  103. if bits == 0 then return 0; end
  104. if bits < 512 then return 20; end
  105. if bits < 1024 then return 40; end
  106. if bits < 2048 then return 80; end
  107. if bits < 4096 then return 90; end
  108. return 100;
  109. elseif keytype == "EC" then
  110. return 100;
  111. end
  112. -- Don't know how to judge DH...
  113. assert(false);
  114. end
  115. local default_params = { mode = "client",
  116. verify = {"peer","fail_if_no_peer_cert"},
  117. verifyext = {"lsec_continue", "crl_check_chain"},
  118. cafile = cafile,
  119. capath = capath,
  120. key = key,
  121. certificate = certificate,
  122. };
  123. function got_sasl(srv_result_id, features_stanza, tls)
  124. if (tls and not sasl_tls_done) or (not tls and not sasl_done) then
  125. local stanza = features_stanza:get_child("mechanisms", "urn:ietf:params:xml:ns:xmpp-sasl");
  126. if stanza then
  127. for k,v in ipairs(stanza) do
  128. if v.name == "mechanism" then
  129. local sth = assert(dbh:prepare("INSERT INTO srv_mechanisms (srv_result_id, mechanism, after_tls) VALUES (?, ?, ?)"));
  130. assert(sth:execute(srv_result_id, v:get_text(), tls));
  131. dbh:commit();
  132. end
  133. end
  134. end
  135. if tls then
  136. sasl_tls_done = true;
  137. else
  138. sasl_done = true;
  139. end
  140. end
  141. end
  142. function got_cert(c, tlsa_answer, srv_result_id)
  143. if not cert_done then
  144. local conn = c.conn:socket();
  145. if not conn.getpeercertificate then
  146. return;
  147. end
  148. local cert = conn:getpeercertificate();
  149. local chain_valid, errors = conn:getpeerverification();
  150. if tlsa_answer then
  151. local matches = { [0] = function (c) return hex(c:der()) end, function (c) return c:digest("sha256") end, function (c) return c:digest("sha512") end }
  152. local matches_spki = { [0] = hex, function (c) return hex(sha256(c)) end, function (c) return hex(sha512(c)) end }
  153. for k,v in ipairs(tlsa_answer) do
  154. v.tlsa.found = false;
  155. if v.tlsa.use == 1 or v.tlsa.use == 3 then
  156. if v.tlsa.select == 0 then
  157. if matches[v.tlsa.match] and (matches[v.tlsa.match](cert) == hex(v.tlsa.data)) then
  158. v.tlsa.found = v.tlsa.use == 3 or chain_valid;
  159. end
  160. elseif v.tlsa.select == 1 then
  161. if matches_spki[v.tlsa.match] and (matches_spki[v.tlsa.match](cert:spki()) == hex(v.tlsa.data)) then
  162. v.tlsa.found = v.tlsa.use == 3 or chain_valid;
  163. end
  164. end
  165. elseif v.tlsa.use == 0 or v.tlsa.use == 2 then
  166. local i = 2;
  167. while true do
  168. local cert = conn:getpeercertificate(i);
  169. if not cert then break end;
  170. if v.tlsa.select == 0 then
  171. if matches[v.tlsa.match] and (matches[v.tlsa.match](cert) == hex(v.tlsa.data)) then
  172. v.tlsa.found = v.tlsa.use == 2 or chain_valid;
  173. end
  174. elseif v.tlsa.select == 1 then
  175. if matches_spki[v.tlsa.match] and (matches_spki[v.tlsa.match](cert:spki()) == hex(v.tlsa.data)) then
  176. v.tlsa.found = v.tlsa.use == 2 or chain_valid;
  177. end
  178. end
  179. i = i + 1;
  180. end
  181. end
  182. end
  183. local stm = assert(dbh:prepare("INSERT INTO tlsa_records (srv_result_id, usage, selector, match, data, verified) VALUES (?, ?, ?, ?, decode(?, 'hex'), ?)"));
  184. for k,v in ipairs(tlsa_answer) do
  185. assert(stm:execute(srv_result_id, v.tlsa.use, v.tlsa.select, v.tlsa.match, hex(v.tlsa.data), v.tlsa.found == true));
  186. end
  187. dbh:commit();
  188. end
  189. local chain_valid, errors = conn:getpeerverification();
  190. local valid_identity = cert_verify_identity(host, "xmpp-"..mode, cert);
  191. if not chain_valid then
  192. fail_untrusted = true;
  193. end
  194. if not valid_identity then
  195. fail_untrusted = true;
  196. end
  197. local _, keytype = cert:pubkey()
  198. if keytype == "RSA" or keytype == "DSA" then
  199. if cert:bits() < 1024 then
  200. fail_1024 = true;
  201. elseif cert:bits() < 2048 then
  202. cap_2048 = true;
  203. end
  204. end
  205. if cert:signature_alg() == "md5WithRSAEncryption" then
  206. fail_md5 = true;
  207. end
  208. local certs = conn:getpeerchain();
  209. local current_cert = cert;
  210. local used_certs = {};
  211. local chain = {};
  212. local i = 1;
  213. while true do
  214. used_certs[i] = true;
  215. local cert_id = sql.insert_cert(dbh, current_cert, srv_result_id, i - 1, errors and errors[i] or {});
  216. chain[#chain + 1] = cert_id;
  217. if #chain > 1 then
  218. local stm = assert(dbh:prepare("UPDATE certificates SET signed_by_id = ? WHERE certificate_id = ?"));
  219. assert(stm:execute(cert_id, chain[#chain - 1]));
  220. dbh:commit();
  221. end
  222. local new_cert = nil;
  223. i = nil;
  224. for k,v in ipairs(certs) do
  225. local res, err = conn:did_issue(v, current_cert);
  226. if res > 0 then
  227. new_cert = v;
  228. i = k;
  229. break;
  230. end
  231. end
  232. if new_cert == nil then
  233. -- Try to find it in the database before we give up.
  234. local q = {};
  235. local args = {}
  236. for k,v in pairs(current_cert:issuer()) do
  237. q[#q + 1] = "SELECT certificate_id FROM certificate_subjects WHERE (name = ? AND value = ?)"
  238. args[#args + 1] = v.name;
  239. args[#args + 1] = v.value;
  240. end
  241. -- We know nothing. Too much to search through, we give up.
  242. if #q == 0 then
  243. break;
  244. end
  245. local query = table.concat(q, " INTERSECT ");
  246. local stm = assert(dbh:prepare(query));
  247. assert(stm:execute(unpack(args)));
  248. local result = stm:fetch();
  249. if not result then
  250. break
  251. end
  252. for k,v in pairs(result) do
  253. local stm = assert(dbh:prepare("SELECT pem FROM certificates WHERE certificate_id = ?"));
  254. assert(stm:execute(v));
  255. local pem = stm:fetch()[1];
  256. local candidate = cert_load(pem);
  257. if conn:did_issue(candidate, current_cert) then
  258. local stm = assert(dbh:prepare("UPDATE certificates SET signed_by_id = ? WHERE certificate_id = ?"));
  259. assert(stm:execute(v, cert_id));
  260. dbh:commit();
  261. end
  262. end
  263. break
  264. end;
  265. if new_cert:pem() == current_cert:pem() then
  266. local stm = assert(dbh:prepare("UPDATE certificates SET signed_by_id = ? WHERE certificate_id = ?"));
  267. assert(stm:execute(cert_id, cert_id));
  268. dbh:commit();
  269. break;
  270. end
  271. if used_certs[i] then
  272. break;
  273. end
  274. current_cert = new_cert;
  275. end
  276. for k,v in ipairs(certs) do
  277. if not used_certs[k] then
  278. sql.insert_cert(dbh, v, srv_result_id, k - 1, errors and errors[k] or {});
  279. end
  280. end
  281. local certificate_score = 0;
  282. if chain_valid and valid_identity then
  283. certificate_score = 100;
  284. end
  285. local pubkey, keytype, bits = cert:pubkey();
  286. public_key_score = keysize_score(cert:pubkey());
  287. local sth = assert(dbh:prepare("UPDATE srv_results SET compression = ?, certificate_score = ?, valid_identity = ?, trusted = ? WHERE srv_result_id = ?"));
  288. assert(sth:execute(conn:info("compression"), certificate_score, valid_identity, chain_valid, srv_result_id));
  289. if conn:info("compression") then
  290. cap_compression = true;
  291. end
  292. dbh:commit();
  293. cert_done = true;
  294. end
  295. end
  296. local features_done = false;
  297. function test_params(target, port, params, tlsa_answer, srv_result_id)
  298. local c = verse.new();
  299. local done = false;
  300. local ssl_done = false;
  301. c.tlsparams = params;
  302. c.connect_host = target;
  303. c.connect_port = port;
  304. if target:find(".onion(.?)$") then
  305. c.connect = function (session, host, port)
  306. return onions.connect_socks5(session, host, port);
  307. end
  308. end
  309. c:hook("outgoing-raw", function (data) return c:debug("OUT: " .. data); end);
  310. c:hook("incoming-raw", function (data) return c:debug("IN: " .. data); end);
  311. c:hook("stream-error", function(event)
  312. if event:get_child("host-unknown", "urn:ietf:params:xml:ns:xmpp-streams") then
  313. local sth = assert(dbh:prepare("UPDATE srv_results SET error = ?, done = 't' WHERE srv_result_id = ? AND error IS NULL"));
  314. assert(sth:execute("This server does not serve " .. jid .. ".", srv_result_id));
  315. dbh:commit();
  316. c:close();
  317. return true;
  318. elseif event:get_child("not-authorized", "urn:ietf:params:xml:ns:xmpp-streams") then
  319. if not done then
  320. done = true;
  321. local info = c.conn:socket():info();
  322. verse.add_task(sleep_for, function ()
  323. assert(coroutine.resume(co, info, "Remote did not trust our cert."));
  324. end);
  325. end
  326. c:close();
  327. return true;
  328. end
  329. end, 1000)
  330. c:hook("stream-features", function (features_stanza)
  331. local stanza = features_stanza:get_child("starttls", "urn:ietf:params:xml:ns:xmpp-tls");
  332. c:debug("Features!");
  333. got_sasl(srv_result_id, features_stanza, ssl_done);
  334. if not ssl_done then
  335. if stanza and stanza:get_child("required") then
  336. if not features_done then
  337. features_done = true;
  338. local sth = assert(dbh:prepare("UPDATE srv_results SET requires_starttls = ? WHERE srv_result_id = ?"));
  339. assert(sth:execute(true, srv_result_id));
  340. dbh:commit();
  341. end
  342. elseif stanza then
  343. if not features_done then
  344. features_done = true;
  345. local sth = assert(dbh:prepare("UPDATE srv_results SET requires_starttls = ? WHERE srv_result_id = ?"));
  346. assert(sth:execute(false, srv_result_id));
  347. dbh:commit();
  348. end
  349. else
  350. if not features_done then
  351. local sth = assert(dbh:prepare("UPDATE srv_results SET error = ?, done = 't' WHERE srv_result_id = ? AND error IS NULL"));
  352. assert(sth:execute("Server does not support encryption.", srv_result_id));
  353. dbh:commit();
  354. features_done = true;
  355. end
  356. done = true;
  357. verse.add_task(sleep_for, function ()
  358. assert(coroutine.resume(co, nil, "No starttls offered"));
  359. end);
  360. end
  361. elseif not done then
  362. done = true;
  363. c:debug("Closing stream");
  364. local info = c.conn:socket():info();
  365. verse.add_task(1, function ()
  366. c:close();
  367. end);
  368. verse.add_task(sleep_for, function ()
  369. assert(coroutine.resume(co, info));
  370. end);
  371. return false;
  372. end
  373. end, 1000);
  374. c:hook("status", function (status)
  375. if status == "ssl-handshake-complete" then
  376. ssl_done = true;
  377. got_cert(c, tlsa_answer, srv_result_id);
  378. end
  379. end, 1000);
  380. c:hook("disconnected", function ()
  381. if not done then
  382. done = true;
  383. verse.add_task(sleep_for, function ()
  384. assert(coroutine.resume(co, nil, "Disconnected"));
  385. end);
  386. end
  387. end);
  388. verse.add_task(30, function ()
  389. if not done then
  390. c:debug("Handshake took 30 seconds. Giving up.");
  391. done = true;
  392. verse.add_task(sleep_for, function ()
  393. assert(coroutine.resume(co, nil, "Timeout"));
  394. end);
  395. end
  396. end);
  397. c:connect_client(jid);
  398. end
  399. local function test_server(target, port, co, tlsa_answer, srv_result_id)
  400. total_score = 0;
  401. public_key_score = 0;
  402. fail_untrusted = false;
  403. fail_ssl2 = false;
  404. cap_ssl3 = false;
  405. local params;
  406. local protocols = {};
  407. local lowest_protocol, highest_protocol;
  408. local info = nil;
  409. params = default_params;
  410. params.options = {"no_sslv3"};
  411. params.protocol = "sslv2";
  412. test_params(target, port, params, tlsa_answer, srv_result_id);
  413. info = coroutine.yield();
  414. if info then
  415. protocols[#protocols + 1] = "sslv2";
  416. lowest_protocol = 20;
  417. highest_protocol = 20;
  418. fail_ssl2 = true;
  419. end
  420. params = deep_copy(default_params);
  421. params.options = {"no_sslv2"};
  422. params.protocol = "sslv3";
  423. test_params(target, port, params, tlsa_answer, srv_result_id);
  424. info = coroutine.yield();
  425. if info then
  426. protocols[#protocols + 1] = "sslv3";
  427. if not lowest_protocol then lowest_protocol = 80; end
  428. highest_protocol = 80;
  429. cap_ssl3 = true;
  430. end
  431. params = deep_copy(default_params);
  432. params.options = {"no_sslv3"};
  433. params.protocol = "tlsv1";
  434. test_params(target, port, params, tlsa_answer, srv_result_id);
  435. info = coroutine.yield();
  436. if info then
  437. protocols[#protocols + 1] = "tlsv1";
  438. if not lowest_protocol then lowest_protocol = 90; end
  439. highest_protocol = 90;
  440. end
  441. params = deep_copy(default_params);
  442. params.options = {"no_sslv3","no_tlsv1"};
  443. params.protocol = "tlsv1_1";
  444. test_params(target, port, params, tlsa_answer, srv_result_id);
  445. info = coroutine.yield();
  446. if info then
  447. protocols[#protocols + 1] = "tlsv1_1";
  448. if not lowest_protocol then lowest_protocol = 95; end
  449. highest_protocol = 95;
  450. end
  451. params = deep_copy(default_params);
  452. params.options = {"no_sslv3","no_tlsv1","no_tlsv1_1"};
  453. params.protocol = "tlsv1_2";
  454. test_params(target, port, params, tlsa_answer, srv_result_id);
  455. info = coroutine.yield();
  456. if info then
  457. protocols[#protocols + 1] = "tlsv1_2";
  458. if not lowest_protocol then lowest_protocol = 100; end
  459. highest_protocol = 100;
  460. end
  461. local sth = assert(dbh:prepare("UPDATE srv_results SET sslv2 = '0', sslv3 = '0', tlsv1 = '0', tlsv1_1 = '0', tlsv1_2 = '0' WHERE srv_result_id = ?;"));
  462. assert(sth:execute(srv_result_id));
  463. if mode == "server" and #protocols > 0 then
  464. params = deep_copy(default_params);
  465. params.key = nil;
  466. params.certificate = nil;
  467. params.protocol = protocols[1];
  468. test_params(target, port, params, tlsa_answer, srv_result_id);
  469. local info, err = coroutine.yield();
  470. if not info or err == "Remote did not trust our cert." then
  471. local sth = assert(dbh:prepare("UPDATE srv_results SET requires_peer_cert = '1' WHERE srv_result_id = ?"));
  472. assert(sth:execute(srv_result_id));
  473. dbh:commit();
  474. end
  475. end
  476. for k,v in ipairs(protocols) do
  477. -- v can only be sslv2, sslv3, tlsv1, tlsv1_1 or tlsv1_2, so this is fine. Really.
  478. local sth = assert(dbh:prepare("UPDATE srv_results SET " .. v .. " = '1' WHERE srv_result_id = ?"));
  479. assert(sth:execute(srv_result_id));
  480. end
  481. dbh:commit();
  482. if #protocols == 0 then
  483. local sth = assert(dbh:prepare("UPDATE srv_results SET error = ?, done = 't' WHERE srv_result_id = ? AND error IS NULL"));
  484. assert(sth:execute("Connection failed.", srv_result_id));
  485. dbh:commit();
  486. return
  487. end
  488. local protocol_score = (lowest_protocol + highest_protocol)/2;
  489. local sth = assert(dbh:prepare("UPDATE srv_results SET protocol_score = ? WHERE srv_result_id = ?"));
  490. assert(sth:execute(protocol_score, srv_result_id));
  491. dbh:commit();
  492. total_score = total_score + 0.3 * protocol_score;
  493. local cipher_string = "ALL:COMPLEMENTOFALL";
  494. local ciphers = {};
  495. local cipher_key_score_override = 100;
  496. for i=#protocols,1,-1 do
  497. local v = protocols[i];
  498. while true do
  499. local params = deep_copy(default_params);
  500. params.protocol = v;
  501. params.ciphers = cipher_string;
  502. test_params(target, port, params);
  503. local info, err = coroutine.yield();
  504. if not info then break end;
  505. ciphers[#ciphers + 1] = info;
  506. log("debug", "Cipher strings: " .. cipher_string .. " cipher: " .. info.cipher);
  507. cipher_string = cipher_string .. ":!" .. info.cipher;
  508. if info.export then
  509. cipher_key_score_override = math.min(cipher_key_score_override, 40);
  510. end
  511. if info.tempalg == "DH" then
  512. if info.tempbits < 512 then
  513. cipher_key_score_override = math.min(cipher_key_score_override, 20);
  514. elseif info.tempbits < 1024 then
  515. cipher_key_score_override = math.min(cipher_key_score_override, 40);
  516. elseif info.tempbits < 2014 then
  517. cipher_key_score_override = math.min(cipher_key_score_override, 80);
  518. elseif info.tempbits < 4096 then
  519. cipher_key_score_override = math.min(cipher_key_score_override, 90);
  520. end
  521. end
  522. if info.authentication == "None" then
  523. cipher_key_score_override = 0;
  524. end
  525. if info.encryption == "RC4(128)" and (v == "tslv1_1" or v == "tlsv1_2") then
  526. warn_rc4_tls11 = true;
  527. end
  528. if info.cipher:find("ECDHE-") == 1 or info.cipher:find("DHE-") == 1 then
  529. warn_no_fs = false;
  530. end
  531. end
  532. end
  533. public_key_score = math.min(public_key_score, cipher_key_score_override);
  534. local sth = assert(dbh:prepare("UPDATE srv_results SET keysize_score = ? WHERE srv_result_id = ?"));
  535. assert(sth:execute(public_key_score, srv_result_id));
  536. dbh:commit();
  537. total_score = total_score + 0.3 * public_key_score;
  538. local should_sort = true;
  539. if #ciphers > 1 then
  540. local cipher1 = ciphers[1];
  541. local cipher2 = ciphers[2];
  542. local protocol = protocols[#protocols];
  543. local params = deep_copy(default_params);
  544. params.protocol = protocol;
  545. params.ciphers = cipher1.cipher .. ":" .. cipher2.cipher;
  546. test_params(target, port, params);
  547. local result1, err1 = coroutine.yield();
  548. local params = deep_copy(default_params);
  549. params.protocol = protocol;
  550. params.ciphers = cipher2.cipher .. ":" .. cipher1.cipher;
  551. test_params(target, port, params);
  552. local result2, err2 = coroutine.yield();
  553. if not result1 or not result2 then
  554. elseif result1.cipher == result2.cipher then
  555. should_sort = false;
  556. local sth = assert(dbh:prepare("UPDATE srv_results SET reorders_ciphers = '1' WHERE srv_result_id = ?"));
  557. assert(sth:execute(srv_result_id));
  558. dbh:commit();
  559. else
  560. local sth = assert(dbh:prepare("UPDATE srv_results SET reorders_ciphers = '0' WHERE srv_result_id = ?"));
  561. assert(sth:execute(srv_result_id));
  562. dbh:commit();
  563. end
  564. end
  565. if should_sort then
  566. table.sort(ciphers, function (a, b)
  567. if a.bits == b.bits then
  568. if a.protocol == b.protocol then
  569. return a.cipher < b.cipher;
  570. else
  571. return a.protocol > b.protocol;
  572. end
  573. else
  574. return a.bits > b.bits;
  575. end
  576. end);
  577. end
  578. local max_bits = 0;
  579. local min_bits = math.huge;
  580. local sth = assert(dbh:prepare("INSERT INTO srv_ciphers (srv_result_id, cipher_id, cipher_index, ecdh_curve, dh_bits, dh_group_id) VALUES (?, ?, ?, ?, ?, ?)"));
  581. local get_dh_group = assert(dbh:prepare("SELECT dh_group_id FROM dh_groups WHERE prime = decode(?, 'hex') AND generator = decode(?, 'hex')"));
  582. for k,v in ipairs(ciphers) do
  583. local dh_group_id = nil
  584. if v.tempalg == "DH" then
  585. assert(get_dh_group:execute(hex(v.dh_p), hex(v.dh_g)));
  586. dbh:commit()
  587. local results = get_dh_group:fetch()
  588. if not results or #results == 0 then
  589. dh_group_id, err = sql.execute_and_get_id(dbh, "INSERT INTO dh_groups (prime, generator) VALUES (decode(?, 'hex'), decode(?, 'hex'))", hex(v.dh_p), hex(v.dh_g));
  590. -- A race condition, great. Lets retry the lookup.
  591. if err then
  592. assert(get_dh_group:execute(hex(v.dh_p), hex(v.dh_g)));
  593. dbh:commit();
  594. dh_group_id = get_dh_group:fetch()[1]
  595. end
  596. else
  597. dh_group_id = results[1]
  598. end
  599. if v.tempbits < 2048 then
  600. cap_dh_2048 = true;
  601. end
  602. end
  603. assert(sth:execute(srv_result_id, ciphertable.find(v.cipher), k - 1, v.curve, v.tempalg == "DH" and v.tempbits or nil, dh_group_id));
  604. if v.bits < min_bits then min_bits = v.bits; end;
  605. if v.bits > max_bits then max_bits = v.bits; end;
  606. end
  607. dbh:commit();
  608. local function cipher_score(bits)
  609. if bits == 0 then return 0 end
  610. if bits < 128 then return 20 end
  611. if bits < 256 then return 80 end
  612. return 100
  613. end
  614. local cipher_score = (cipher_score(max_bits) + cipher_score(min_bits))/2;
  615. total_score = total_score + 0.4 * cipher_score;
  616. local sth = assert(dbh:prepare("UPDATE srv_results SET cipher_score = ? WHERE srv_result_id = ?"));
  617. assert(sth:execute(cipher_score, srv_result_id));
  618. dbh:commit();
  619. local function grade(score)
  620. if score >= 80 then return "A"; end
  621. if score >= 65 then return "B"; end
  622. if score >= 50 then return "C"; end
  623. if score >= 35 then return "D"; end
  624. if score >= 20 then return "E"; end
  625. return "F";
  626. end
  627. local final_grade = grade(total_score);
  628. if fail_ssl2 then
  629. final_grade = "F";
  630. end
  631. if fail_1024 then
  632. final_grade = "F";
  633. end
  634. if fail_md5 then
  635. final_grade = "F";
  636. end
  637. -- Fail servers that have SSL3 as their best protocol.
  638. if highest_protocol == 80 then
  639. final_grade = "F";
  640. end
  641. if cap_2048 then
  642. if final_grade == "A" then
  643. final_grade = "B";
  644. end
  645. end
  646. if cap_ssl3 then
  647. if final_grade == "A" then
  648. final_grade = "B"
  649. end
  650. end
  651. if cap_dh_2048 then
  652. if final_grade == "A" then
  653. final_grade = "B";
  654. end
  655. end
  656. if cap_compression then
  657. if final_grade == "A" or final_grade == "B" then
  658. final_grade = "C";
  659. end
  660. end
  661. -- Cap to C if RC4 is used with TLS 1.1+.
  662. if warn_rc4_tls11 then
  663. if final_grade == "A" or final_grade == "B" then
  664. final_grade = "C";
  665. end
  666. end
  667. -- Cap to C if not supporting TLS 1.2.
  668. if not (highest_protocol == 100) then
  669. if final_grade == "A" or final_grade == "B" then
  670. final_grade = "C";
  671. end
  672. end
  673. local sth = assert(dbh:prepare("UPDATE srv_results SET total_score = ?, grade = ?, done = '1', warn_rc4_tls11 = ?, warn_no_fs = ?, warn_dh_2048 = ? WHERE srv_result_id = ?"));
  674. assert(sth:execute(total_score, final_grade, warn_rc4_tls11, warn_no_fs, cap_dh_2048, srv_result_id));
  675. dbh:commit();
  676. return nil;
  677. end
  678. co = coroutine.create(function ()
  679. log("debug", "Starting DNS lookup.")
  680. local srv_records, err = adns.dns.lookup("_xmpp-" .. mode .. "._tcp." .. to_ascii(host), "SRV");
  681. if err then
  682. log("debug", "Resolving failed with error: " .. er)
  683. os.exit()
  684. end
  685. if version_jid and version_password then
  686. local version = require("verse").init("client").new();
  687. version:add_plugin("version");
  688. version:connect_client(version_jid, version_password);
  689. local done = false;
  690. version:hook("ready", function ()
  691. version:query_version(host, function (v)
  692. if not done then
  693. assert(coroutine.resume(co, (v.name or "unknown") .. " " .. (v.version or "unknown")));
  694. version:close();
  695. done = true;
  696. end
  697. end);
  698. end);
  699. verse.add_task(15, function ()
  700. if not done then
  701. coroutine.resume(co);
  702. version:close();
  703. done = true;
  704. end
  705. end);
  706. local result = coroutine.yield();
  707. local stm = assert(dbh:prepare("UPDATE test_results SET version = ? WHERE test_id = ?"));
  708. assert(stm:execute(result, result_id));
  709. package.loaded["verse.client"] = nil;
  710. end
  711. verse = require("verse").init(mode);
  712. local stm = assert(dbh:prepare("UPDATE test_results SET srv_dnssec_good = ?, srv_dnssec_bogus = ? WHERE test_id = ?"));
  713. assert(stm:execute(srv_records and srv_records.secure, srv_records and not not srv_records.bogus, result_id));
  714. if not (srv_records and #srv_records > 0) then
  715. local port = (mode == "client" and 5222) or 5269;
  716. srv_records = { { srv = { port = port, target = host } } };
  717. end
  718. local q = "INSERT INTO srv_results (test_id, priority, weight, port, target, cipher_score, certificate_score, keysize_score, protocol_score, total_score, requires_peer_cert, done, tlsa_dnssec_good, tlsa_dnssec_bogus) " ..
  719. "VALUES (?, ?, ?, ?, ?, 0, 0, 0, 0, 0.0, '0', '0', ?, ?)";
  720. for k,v in ipairs(srv_records) do
  721. local srv = v.srv;
  722. features_done = false;
  723. cert_done = false;
  724. sasl_done = false;
  725. sasl_tls_done = false;
  726. local tlsa = "_" .. srv.port .. "._tcp." .. srv.target;
  727. local tlsa_supported = (not require("net.dns").types) or (require("net.dns").types[52] == "TLSA");
  728. local tlsa_answer = nil;
  729. local srv_id;
  730. if tlsa_supported then
  731. local err
  732. tlsa_answer, err = adns.dns.lookup(tlsa, "TLSA");
  733. srv_id = assert(sql.execute_and_get_id(dbh, q, result_id, srv.priority, srv.weight, srv.port, srv.target, tlsa_answer.secure, not not tlsa_answer.bogus));
  734. dbh:commit();
  735. else
  736. srv_id = assert(sql.execute_and_get_id(dbh, q, result_id, srv.priority, srv.weight, srv.port, srv.target, nil, nil));
  737. end
  738. test_server(srv.target, srv.port, co, tlsa_answer, srv_id);
  739. end
  740. dbh:commit();
  741. dbh:close();
  742. os.exit();
  743. end)
  744. verse.add_task(0, function ()
  745. assert(coroutine.resume(co));
  746. end);
  747. verse.add_task(30 * 60, function ()
  748. os.exit();
  749. end);
  750. verse.loop();