| 1 | -- Prosody IM |
| 2 | -- Copyright (C) 2008-2010 Matthew Wild |
| 3 | -- Copyright (C) 2008-2010 Waqas Hussain |
| 4 | -- |
| 5 | -- This project is MIT/X11 licensed. Please see the |
| 6 | -- COPYING file in the source package for more information. |
| 7 | -- |
| 8 | |
| 9 | local config = require "core.configmanager"; |
| 10 | local create_context = require "core.certmanager".create_context; |
| 11 | local st = require "util.stanza"; |
| 12 | |
| 13 | local secure_auth_only = module:get_option("c2s_require_encryption") or module:get_option("require_encryption"); |
| 14 | local secure_s2s_only = module:get_option("s2s_require_encryption"); |
| 15 | local allow_s2s_tls = module:get_option("s2s_allow_encryption") ~= false; |
| 16 | |
| 17 | local xmlns_starttls = 'urn:ietf:params:xml:ns:xmpp-tls'; |
| 18 | local starttls_attr = { xmlns = xmlns_starttls }; |
| 19 | local starttls_proceed = st.stanza("proceed", starttls_attr); |
| 20 | local starttls_failure = st.stanza("failure", starttls_attr); |
| 21 | local c2s_feature = st.stanza("starttls", starttls_attr); |
| 22 | local s2s_feature = st.stanza("starttls", starttls_attr); |
| 23 | if secure_auth_only then c2s_feature:tag("required"):up(); end |
| 24 | if secure_s2s_only then s2s_feature:tag("required"):up(); end |
| 25 | |
| 26 | local global_ssl_ctx = prosody.global_ssl_ctx; |
| 27 | |
| 28 | local host = hosts[module.host]; |
| 29 | |
| 30 | local function can_do_tls(session) |
| 31 | if session.type == "c2s_unauthed" then |
| 32 | return session.conn.starttls and host.ssl_ctx_in; |
| 33 | elseif session.type == "s2sin_unauthed" and allow_s2s_tls then |
| 34 | return session.conn.starttls and host.ssl_ctx_in; |
| 35 | elseif session.direction == "outgoing" and allow_s2s_tls then |
| 36 | return session.conn.starttls and host.ssl_ctx; |
| 37 | end |
| 38 | return false; |
| 39 | end |
| 40 | |
| 41 | -- Hook <starttls/> |
| 42 | module:hook("stanza/urn:ietf:params:xml:ns:xmpp-tls:starttls", function(event) |
| 43 | local origin = event.origin; |
| 44 | if can_do_tls(origin) then |
| 45 | (origin.sends2s or origin.send)(starttls_proceed); |
| 46 | origin:reset_stream(); |
| 47 | local host = origin.to_host or origin.host; |
| 48 | local ssl_ctx = host and hosts[host].ssl_ctx_in or global_ssl_ctx; |
| 49 | origin.conn:starttls(ssl_ctx); |
| 50 | origin.log("info", "TLS negotiation started for %s...", origin.type); |
| 51 | origin.secure = false; |
| 52 | else |
| 53 | origin.log("warn", "Attempt to start TLS, but TLS is not available on this %s connection", origin.type); |
| 54 | (origin.sends2s or origin.send)(starttls_failure); |
| 55 | origin:close(); |
| 56 | end |
| 57 | return true; |
| 58 | end); |
| 59 | |
| 60 | -- Advertize stream feature |
| 61 | module:hook("stream-features", function(event) |
| 62 | local origin, features = event.origin, event.features; |
| 63 | if can_do_tls(origin) then |
| 64 | features:add_child(c2s_feature); |
| 65 | end |
| 66 | end); |
| 67 | module:hook("s2s-stream-features", function(event) |
| 68 | local origin, features = event.origin, event.features; |
| 69 | if can_do_tls(origin) then |
| 70 | features:add_child(s2s_feature); |
| 71 | end |
| 72 | end); |
| 73 | |
| 74 | -- For s2sout connections, start TLS if we can |
| 75 | module:hook_stanza("http://etherx.jabber.org/streams", "features", function (session, stanza) |
| 76 | module:log("debug", "Received features element"); |
| 77 | if can_do_tls(session) and stanza:child_with_ns(xmlns_starttls) then |
| 78 | module:log("%s is offering TLS, taking up the offer...", session.to_host); |
| 79 | session.sends2s("<starttls xmlns='"..xmlns_starttls.."'/>"); |
| 80 | return true; |
| 81 | end |
| 82 | end, 500); |
| 83 | |
| 84 | module:hook_stanza(xmlns_starttls, "proceed", function (session, stanza) |
| 85 | module:log("debug", "Proceeding with TLS on s2sout..."); |
| 86 | session:reset_stream(); |
| 87 | local ssl_ctx = session.from_host and hosts[session.from_host].ssl_ctx or global_ssl_ctx; |
| 88 | session.conn:starttls(ssl_ctx); |
| 89 | session.secure = false; |
| 90 | return true; |
| 91 | end); |
| 92 | |
| 93 | function module.load() |
| 94 | local global_ssl_config = config.get("*", "core", "ssl"); |
| 95 | local ssl_config = config.get(module.host, "core", "ssl"); |
| 96 | local base_host = module.host:match("%.(.*)"); |
| 97 | if ssl_config == global_ssl_config and hosts[base_host] then |
| 98 | ssl_config = config.get(base_host, "core", "ssl"); |
| 99 | end |
| 100 | host.ssl_ctx = create_context(host.host, "client", ssl_config); -- for outgoing connections |
| 101 | host.ssl_ctx_in = create_context(host.host, "server", ssl_config); -- for incoming connections |
| 102 | end |
| 103 | |
| 104 | function module.unload() |
| 105 | host.ssl_ctx = nil; |
| 106 | host.ssl_ctx_in = nil; |
| 107 | end |