mod_sasl2_fast/mod_sasl2_fast.lua
changeset 5070 74145faceba2
parent 5066 38a0e3621181
child 5072 20e635eb4cdc
equal deleted inserted replaced
5069:368bf9b06484 5070:74145faceba2
     1 local tokenauth = module:depends("tokenauth");
       
     2 local sasl = require "util.sasl";
     1 local sasl = require "util.sasl";
     3 local dt = require "util.datetime";
     2 local dt = require "util.datetime";
       
     3 local id = require "util.id";
     4 local st = require "util.stanza";
     4 local st = require "util.stanza";
       
     5 local now = require "util.time".now;
       
     6 local hash = require "util.hashes";
     5 
     7 
     6 local fast_token_ttl = module:get_option_number("sasl2_fast_token_ttl", 86400*21);
     8 local fast_token_ttl = module:get_option_number("sasl2_fast_token_ttl", 86400*21);
     7 
     9 
     8 local xmlns_fast = "urn:xmpp:fast:0";
    10 local xmlns_fast = "urn:xmpp:fast:0";
     9 local xmlns_sasl2 = "urn:xmpp:sasl:2";
    11 local xmlns_sasl2 = "urn:xmpp:sasl:2";
    10 
    12 
    11 function get_sasl_handler(session) --luacheck: ignore session
    13 local token_store = module:open_store("fast_tokens", "map");
       
    14 
       
    15 local function make_token(username, client_id, mechanism)
       
    16 	local new_token = "secret-token:fast-"..id.long();
       
    17 	local key = hash.sha256(client_id, true).."-new";
       
    18 	local issued_at = now();
       
    19 	token_store:set(username, key, {
       
    20 		mechanism = mechanism;
       
    21 		secret = new_token;
       
    22 		issued_at = issued_at;
       
    23 		expires_at = issued_at + fast_token_ttl;
       
    24 	});
       
    25 end
       
    26 
       
    27 local function new_token_tester(username, hmac_f)
       
    28 	return function (mechanism, client_id, token_hash, cb_data)
       
    29 		local tried_current_token = false;
       
    30 		local key = hash.sha256(client_id, true).."-new";
       
    31 		local token;
       
    32 		repeat
       
    33 			token = token_store:get(username, key);
       
    34 			if token and token.mechanism == mechanism then
       
    35 				local expected_hash = hmac_f(token.secret, "Initiator"..cb_data);
       
    36 				if hash.equals(expected_hash, token_hash) then
       
    37 					if token.expires_at < now() then
       
    38 						token_store:set(username, key, nil);
       
    39 						return nil, "credentials-expired";
       
    40 					end
       
    41 					if not tried_current_token then
       
    42 						-- The new token is becoming the current token
       
    43 						token_store:set_keys(username, {
       
    44 							[key] = token_store.remove;
       
    45 							[key:sub(1, -4).."-cur"] = token;
       
    46 						});
       
    47 					end
       
    48 					return true, username, hmac_f(token.secret, "Responder"..cb_data);
       
    49 				end
       
    50 			end
       
    51 			if not tried_current_token then
       
    52 				-- Try again with the current token instead
       
    53 				tried_current_token = true;
       
    54 				key = key:sub(1, -4).."-cur";
       
    55 			else
       
    56 				return nil;
       
    57 			end
       
    58 		until false;
       
    59 	end
       
    60 end
       
    61 
       
    62 function get_sasl_handler(session)
       
    63 	local username = session.username;
    12 	local token_auth_profile = {
    64 	local token_auth_profile = {
       
    65 		ht_256 = new_token_tester(username, hash.hmac_sha256);
    13 		token_test = function (_, client_id, token, mech_name, counter) --luacheck: ignore
    66 		token_test = function (_, client_id, token, mech_name, counter) --luacheck: ignore
    14 			return false; -- FIXME
    67 			return false; -- FIXME
    15 		end;
    68 		end;
    16 	};
    69 	};
    17 	return sasl.new(module.host, token_auth_profile);
    70 	return sasl.new(module.host, token_auth_profile);
    18 end
    71 end
    19 
    72 
    20 -- Advertise FAST to connecting clients
    73 -- Advertise FAST to connecting clients
    21 module:hook("advertise-sasl-features", function (event)
    74 module:hook("advertise-sasl-features", function (event)
    22 	local sasl_handler = get_sasl_handler(event.session);
    75 	local session = event.origin;
       
    76 	local sasl_handler = get_sasl_handler(session);
    23 	if not sasl_handler then return; end
    77 	if not sasl_handler then return; end
    24 	event.session.fast_sasl_handler = sasl_handler;
    78 	session.fast_sasl_handler = sasl_handler;
    25 	local fast = st.stanza("fast", { xmlns = xmlns_fast });
    79 	local fast = st.stanza("fast", { xmlns = xmlns_fast });
    26 	for mech in sasl_handler:mechanisms() do
    80 	for mech in pairs(sasl_handler:mechanisms()) do
    27 		fast:text_tag("mechanism", mech);
    81 		fast:text_tag("mechanism", mech);
    28 	end
    82 	end
    29 	event.features:add_child(fast);
    83 	event.features:add_child(fast);
    30 end);
    84 end);
    31 
    85 
    34 	-- Cache action for future processing (after auth success)
    88 	-- Cache action for future processing (after auth success)
    35 	local fast_auth = auth:get_child(xmlns_fast, "fast");
    89 	local fast_auth = auth:get_child(xmlns_fast, "fast");
    36 	if fast_auth then
    90 	if fast_auth then
    37 		-- Client says it is using FAST auth, so set our SASL handler
    91 		-- Client says it is using FAST auth, so set our SASL handler
    38 		session.log("debug", "Client is authenticating using FAST");
    92 		session.log("debug", "Client is authenticating using FAST");
    39 		session.sasl_handler = session.fast_sasl_handler;
    93 		local fast_sasl_handler = session.fast_sasl_handler;
       
    94 		fast_sasl_handler.profile._client_id = session.client_id;
       
    95 		session.sasl_handler = fast_sasl_handler;
    40 	end
    96 	end
    41 	session.fast_sasl_handler = nil;
    97 	session.fast_sasl_handler = nil;
    42 	local fast_token_request = auth:get_child(xmlns_fast, "request-token");
    98 	local fast_token_request = auth:get_child(xmlns_fast, "request-token");
    43 	if fast_token_request then
    99 	if fast_token_request then
    44 		local mech = fast_token_request.attr.mechanism;
   100 		local mech = fast_token_request.attr.mechanism;
    52 -- Process post-success (new token generation, etc.)
   108 -- Process post-success (new token generation, etc.)
    53 module:hook("sasl2/c2s/success", function (event)
   109 module:hook("sasl2/c2s/success", function (event)
    54 	local session = event.session;
   110 	local session = event.session;
    55 
   111 
    56 	local token_request = session.fast_token_request;
   112 	local token_request = session.fast_token_request;
       
   113 	local client_id = session.client_id;
       
   114 	local stream_from = event.stream.from;
    57 	if token_request then
   115 	if token_request then
    58 		local token, token_info = tokenauth.create_jid_token(
   116 		if not client_id or not stream_from then
    59 			session.full_jid,
   117 			session.log("warn", "FAST token requested, but missing client id");
    60 			session.full_jid,
   118 			return;
    61 			session.role,
   119 		end
    62 			fast_token_ttl,
   120 		local token_info = make_token(session.username, client_id, token_request.mechanism)
    63 			{
   121 		if token_info then
    64 				fast_token = true;
       
    65 				fast_mechanism = token_request.mechanism;
       
    66 			}
       
    67 		);
       
    68 		if token then
       
    69 			event.success:tag("token", {
   122 			event.success:tag("token", {
    70 				xmlns = xmlns_fast;
   123 				xmlns = xmlns_fast;
    71 				expiry = dt.datetime(token_info.expiry);
   124 				expiry = dt.datetime(token_info.expires_at);
    72 				token = token;
   125 				token = token_info.token;
    73 			}):up();
   126 			}):up();
    74 		end
   127 		end
    75 	end
   128 	end
    76 end, 75);
   129 end, 75);
    77 
   130 
    84 	end
   137 	end
    85 	return nil, "temporary-auth-failure"; -- FIXME
   138 	return nil, "temporary-auth-failure"; -- FIXME
    86 end
   139 end
    87 
   140 
    88 sasl.registerMechanism("X-PLAIN-TOKEN", { "token_test" }, x_plain_token);
   141 sasl.registerMechanism("X-PLAIN-TOKEN", { "token_test" }, x_plain_token);
       
   142 
       
   143 
       
   144 -- HT-* mechanisms
       
   145 
       
   146 local function new_ht_mechanism(mechanism_name, backend_profile_name, cb_name)
       
   147 	return function (sasl_handler, message)
       
   148 		local backend = sasl_handler.profile[backend_profile_name];
       
   149 		local ok, status, response = backend(mechanism_name, sasl_handler._client_id, message, cb_name and sasl_handler.profile.cb[cb_name] or "");
       
   150 		if not ok then
       
   151 			return "failure", status or "not-authorized";
       
   152 		end
       
   153 		return "success", response;
       
   154 	end
       
   155 end
       
   156 
       
   157 local function register_ht_mechanism(name, backend_profile_name, cb_name)
       
   158 	return sasl.registerMechanism(name, { backend_profile_name }, new_ht_mechanism(
       
   159 		name,
       
   160 		backend_profile_name,
       
   161 		cb_name
       
   162 	));
       
   163 end
       
   164 
       
   165 register_ht_mechanism("HT-SHA-256-NONE", "ht_sha256", nil);
       
   166 register_ht_mechanism("HT-SHA-256-UNIQ", "ht_sha256", "tls-unique");
       
   167 register_ht_mechanism("HT-SHA-256-ENDP", "ht_sha256", "tls-endpoint");
       
   168 register_ht_mechanism("HT-SHA-256-EXPR", "ht_sha256", "tls-exporter");