mod_password_policy/mod_password_policy.lua
author Kim Alvefur <zash@zash.se>
Mon, 07 Feb 2022 16:52:19 +0100
changeset 4891 806f7c8d830b
parent 4836 bfd4af4caddc
child 5822 d3b69859553a
permissions -rw-r--r--
mod_ping_muc: Remove 'kick' status code The intent is "you fell off", not "you were kicked out", however older clients may not recognise the 333 code, but that will have to be an acceptable loss.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
841
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
     1
-- Password policy enforcement for Prosody
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
     2
--
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
     3
-- Copyright (C) 2012 Waqas Hussain
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
     4
--
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
     5
--
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
     6
-- Configuration:
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
     7
--    password_policy = {
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
     8
--        length = 8;
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
     9
--    }
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
    10
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
    11
4836
bfd4af4caddc mod_password_policy: Support for additional policies provided by other modules
Matthew Wild <mwild1@gmail.com>
parents: 4835
diff changeset
    12
local it = require "util.iterators";
bfd4af4caddc mod_password_policy: Support for additional policies provided by other modules
Matthew Wild <mwild1@gmail.com>
parents: 4835
diff changeset
    13
local set = require "util.set";
bfd4af4caddc mod_password_policy: Support for additional policies provided by other modules
Matthew Wild <mwild1@gmail.com>
parents: 4835
diff changeset
    14
local st = require "util.stanza";
bfd4af4caddc mod_password_policy: Support for additional policies provided by other modules
Matthew Wild <mwild1@gmail.com>
parents: 4835
diff changeset
    15
841
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
    16
local options = module:get_option("password_policy");
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
    17
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
    18
options = options or {};
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
    19
options.length = options.length or 8;
4833
caf7e88dc9e5 mod_password_policy: Add check that password doesn't contain username
Matthew Wild <mwild1@gmail.com>
parents: 4832
diff changeset
    20
if options.exclude_username == nil then
caf7e88dc9e5 mod_password_policy: Add check that password doesn't contain username
Matthew Wild <mwild1@gmail.com>
parents: 4832
diff changeset
    21
	options.exclude_username = true;
caf7e88dc9e5 mod_password_policy: Add check that password doesn't contain username
Matthew Wild <mwild1@gmail.com>
parents: 4832
diff changeset
    22
end
841
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
    23
4836
bfd4af4caddc mod_password_policy: Support for additional policies provided by other modules
Matthew Wild <mwild1@gmail.com>
parents: 4835
diff changeset
    24
local builtin_policies = set.new({ "length", "exclude_username" });
bfd4af4caddc mod_password_policy: Support for additional policies provided by other modules
Matthew Wild <mwild1@gmail.com>
parents: 4835
diff changeset
    25
local extra_policies = set.new(it.to_array(it.keys(options))) - builtin_policies;
bfd4af4caddc mod_password_policy: Support for additional policies provided by other modules
Matthew Wild <mwild1@gmail.com>
parents: 4835
diff changeset
    26
bfd4af4caddc mod_password_policy: Support for additional policies provided by other modules
Matthew Wild <mwild1@gmail.com>
parents: 4835
diff changeset
    27
local extra_policy_handlers = {};
bfd4af4caddc mod_password_policy: Support for additional policies provided by other modules
Matthew Wild <mwild1@gmail.com>
parents: 4835
diff changeset
    28
bfd4af4caddc mod_password_policy: Support for additional policies provided by other modules
Matthew Wild <mwild1@gmail.com>
parents: 4835
diff changeset
    29
module:handle_items("password-policy-provider", function (event)
bfd4af4caddc mod_password_policy: Support for additional policies provided by other modules
Matthew Wild <mwild1@gmail.com>
parents: 4835
diff changeset
    30
	-- Password policy handler added
bfd4af4caddc mod_password_policy: Support for additional policies provided by other modules
Matthew Wild <mwild1@gmail.com>
parents: 4835
diff changeset
    31
	local item = event.item;
bfd4af4caddc mod_password_policy: Support for additional policies provided by other modules
Matthew Wild <mwild1@gmail.com>
parents: 4835
diff changeset
    32
	module:log("error", "Adding password policy handler '%s'", item.name);
bfd4af4caddc mod_password_policy: Support for additional policies provided by other modules
Matthew Wild <mwild1@gmail.com>
parents: 4835
diff changeset
    33
	extra_policy_handlers[item.name] = item.check_password;
bfd4af4caddc mod_password_policy: Support for additional policies provided by other modules
Matthew Wild <mwild1@gmail.com>
parents: 4835
diff changeset
    34
end, function (event)
bfd4af4caddc mod_password_policy: Support for additional policies provided by other modules
Matthew Wild <mwild1@gmail.com>
parents: 4835
diff changeset
    35
	-- Password policy handler removed
bfd4af4caddc mod_password_policy: Support for additional policies provided by other modules
Matthew Wild <mwild1@gmail.com>
parents: 4835
diff changeset
    36
	local item = event.item;
bfd4af4caddc mod_password_policy: Support for additional policies provided by other modules
Matthew Wild <mwild1@gmail.com>
parents: 4835
diff changeset
    37
	extra_policy_handlers[item.name] = nil;
bfd4af4caddc mod_password_policy: Support for additional policies provided by other modules
Matthew Wild <mwild1@gmail.com>
parents: 4835
diff changeset
    38
end);
841
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
    39
4833
caf7e88dc9e5 mod_password_policy: Add check that password doesn't contain username
Matthew Wild <mwild1@gmail.com>
parents: 4832
diff changeset
    40
function check_password(password, additional_info)
4834
af6143cf7d22 mod_password_policy: Hard failure on missing/empty passwords
Matthew Wild <mwild1@gmail.com>
parents: 4833
diff changeset
    41
	if not password or password == "" then
af6143cf7d22 mod_password_policy: Hard failure on missing/empty passwords
Matthew Wild <mwild1@gmail.com>
parents: 4833
diff changeset
    42
		return nil, "No password provided", "no-password";
af6143cf7d22 mod_password_policy: Hard failure on missing/empty passwords
Matthew Wild <mwild1@gmail.com>
parents: 4833
diff changeset
    43
	end
af6143cf7d22 mod_password_policy: Hard failure on missing/empty passwords
Matthew Wild <mwild1@gmail.com>
parents: 4833
diff changeset
    44
3354
cb26d04b391c mod_password_policy: Return error as second result explaining failure reason
Matthew Wild <mwild1@gmail.com>
parents: 845
diff changeset
    45
	if #password < options.length then
4832
56eba4bca28f mod_password_policy: Allow check_password() to indicate the policy that failed
Matthew Wild <mwild1@gmail.com>
parents: 3355
diff changeset
    46
		return nil, ("Password is too short (minimum %d characters)"):format(options.length), "length";
3354
cb26d04b391c mod_password_policy: Return error as second result explaining failure reason
Matthew Wild <mwild1@gmail.com>
parents: 845
diff changeset
    47
	end
4833
caf7e88dc9e5 mod_password_policy: Add check that password doesn't contain username
Matthew Wild <mwild1@gmail.com>
parents: 4832
diff changeset
    48
caf7e88dc9e5 mod_password_policy: Add check that password doesn't contain username
Matthew Wild <mwild1@gmail.com>
parents: 4832
diff changeset
    49
	if additional_info then
caf7e88dc9e5 mod_password_policy: Add check that password doesn't contain username
Matthew Wild <mwild1@gmail.com>
parents: 4832
diff changeset
    50
		local username = additional_info.username;
caf7e88dc9e5 mod_password_policy: Add check that password doesn't contain username
Matthew Wild <mwild1@gmail.com>
parents: 4832
diff changeset
    51
		if username and password:lower():find(username:lower(), 1, true) then
caf7e88dc9e5 mod_password_policy: Add check that password doesn't contain username
Matthew Wild <mwild1@gmail.com>
parents: 4832
diff changeset
    52
			return nil, "Password must not include your username", "username";
caf7e88dc9e5 mod_password_policy: Add check that password doesn't contain username
Matthew Wild <mwild1@gmail.com>
parents: 4832
diff changeset
    53
		end
caf7e88dc9e5 mod_password_policy: Add check that password doesn't contain username
Matthew Wild <mwild1@gmail.com>
parents: 4832
diff changeset
    54
	end
4836
bfd4af4caddc mod_password_policy: Support for additional policies provided by other modules
Matthew Wild <mwild1@gmail.com>
parents: 4835
diff changeset
    55
bfd4af4caddc mod_password_policy: Support for additional policies provided by other modules
Matthew Wild <mwild1@gmail.com>
parents: 4835
diff changeset
    56
	for policy in extra_policies do
bfd4af4caddc mod_password_policy: Support for additional policies provided by other modules
Matthew Wild <mwild1@gmail.com>
parents: 4835
diff changeset
    57
		local handler = extra_policy_handlers[policy];
bfd4af4caddc mod_password_policy: Support for additional policies provided by other modules
Matthew Wild <mwild1@gmail.com>
parents: 4835
diff changeset
    58
		if not handler then
bfd4af4caddc mod_password_policy: Support for additional policies provided by other modules
Matthew Wild <mwild1@gmail.com>
parents: 4835
diff changeset
    59
			module:log("error", "No policy handler found for '%s' (typo, or module not loaded?)", policy);
bfd4af4caddc mod_password_policy: Support for additional policies provided by other modules
Matthew Wild <mwild1@gmail.com>
parents: 4835
diff changeset
    60
			return nil, "Internal error while verifying password", "internal";
bfd4af4caddc mod_password_policy: Support for additional policies provided by other modules
Matthew Wild <mwild1@gmail.com>
parents: 4835
diff changeset
    61
		end
bfd4af4caddc mod_password_policy: Support for additional policies provided by other modules
Matthew Wild <mwild1@gmail.com>
parents: 4835
diff changeset
    62
		local ok, reason_text, reason_name = handler(password, options[policy], additional_info);
bfd4af4caddc mod_password_policy: Support for additional policies provided by other modules
Matthew Wild <mwild1@gmail.com>
parents: 4835
diff changeset
    63
		if ok ~= true then
bfd4af4caddc mod_password_policy: Support for additional policies provided by other modules
Matthew Wild <mwild1@gmail.com>
parents: 4835
diff changeset
    64
			return nil, reason_text or ("Password failed %s check"):format(policy), reason_name or policy;
bfd4af4caddc mod_password_policy: Support for additional policies provided by other modules
Matthew Wild <mwild1@gmail.com>
parents: 4835
diff changeset
    65
		end
bfd4af4caddc mod_password_policy: Support for additional policies provided by other modules
Matthew Wild <mwild1@gmail.com>
parents: 4835
diff changeset
    66
	end
bfd4af4caddc mod_password_policy: Support for additional policies provided by other modules
Matthew Wild <mwild1@gmail.com>
parents: 4835
diff changeset
    67
3354
cb26d04b391c mod_password_policy: Return error as second result explaining failure reason
Matthew Wild <mwild1@gmail.com>
parents: 845
diff changeset
    68
	return true;
841
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
    69
end
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
    70
4835
5a42cb84c8ee mod_password_policy: Silence luacheck warning for intentional global
Matthew Wild <mwild1@gmail.com>
parents: 4834
diff changeset
    71
function get_policy() --luacheck: ignore 131/get_policy
3355
662f2722f745 mod_password_policy: Export function to get policy in use by the module
Matthew Wild <mwild1@gmail.com>
parents: 3354
diff changeset
    72
	return options;
662f2722f745 mod_password_policy: Export function to get policy in use by the module
Matthew Wild <mwild1@gmail.com>
parents: 3354
diff changeset
    73
end
662f2722f745 mod_password_policy: Export function to get policy in use by the module
Matthew Wild <mwild1@gmail.com>
parents: 3354
diff changeset
    74
841
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
    75
function handler(event)
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
    76
	local origin, stanza = event.origin, event.stanza;
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
    77
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
    78
	if stanza.attr.type == "set" then
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
    79
		local query = stanza.tags[1];
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
    80
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
    81
		local passwords = {};
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
    82
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
    83
		local dataform = query:get_child("x", "jabber:x:data");
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
    84
		if dataform then
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
    85
			for _,tag in ipairs(dataform.tags) do
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
    86
				if tag.attr.var == "password" then
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
    87
					table.insert(passwords, tag:get_child_text("value"));
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
    88
				end
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
    89
			end
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
    90
		end
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
    91
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
    92
		table.insert(passwords, query:get_child_text("password"));
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
    93
4833
caf7e88dc9e5 mod_password_policy: Add check that password doesn't contain username
Matthew Wild <mwild1@gmail.com>
parents: 4832
diff changeset
    94
		local additional_info = {
caf7e88dc9e5 mod_password_policy: Add check that password doesn't contain username
Matthew Wild <mwild1@gmail.com>
parents: 4832
diff changeset
    95
			username = origin.username;
caf7e88dc9e5 mod_password_policy: Add check that password doesn't contain username
Matthew Wild <mwild1@gmail.com>
parents: 4832
diff changeset
    96
		};
caf7e88dc9e5 mod_password_policy: Add check that password doesn't contain username
Matthew Wild <mwild1@gmail.com>
parents: 4832
diff changeset
    97
841
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
    98
		for _,password in ipairs(passwords) do
4832
56eba4bca28f mod_password_policy: Allow check_password() to indicate the policy that failed
Matthew Wild <mwild1@gmail.com>
parents: 3355
diff changeset
    99
			if password then
4833
caf7e88dc9e5 mod_password_policy: Add check that password doesn't contain username
Matthew Wild <mwild1@gmail.com>
parents: 4832
diff changeset
   100
				local pw_ok, pw_err, pw_failed_policy = check_password(password, additional_info);
4832
56eba4bca28f mod_password_policy: Allow check_password() to indicate the policy that failed
Matthew Wild <mwild1@gmail.com>
parents: 3355
diff changeset
   101
				if not pw_ok then
56eba4bca28f mod_password_policy: Allow check_password() to indicate the policy that failed
Matthew Wild <mwild1@gmail.com>
parents: 3355
diff changeset
   102
					module:log("debug", "Password failed check against '%s' policy", pw_failed_policy);
56eba4bca28f mod_password_policy: Allow check_password() to indicate the policy that failed
Matthew Wild <mwild1@gmail.com>
parents: 3355
diff changeset
   103
					origin.send(st.error_reply(stanza, "cancel", "not-acceptable", pw_err));
56eba4bca28f mod_password_policy: Allow check_password() to indicate the policy that failed
Matthew Wild <mwild1@gmail.com>
parents: 3355
diff changeset
   104
					return true;
56eba4bca28f mod_password_policy: Allow check_password() to indicate the policy that failed
Matthew Wild <mwild1@gmail.com>
parents: 3355
diff changeset
   105
				end
841
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
   106
			end
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
   107
		end
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
   108
	end
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
   109
end
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
   110
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
   111
module:hook("iq/self/jabber:iq:register:query", handler, 10);
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
   112
module:hook("iq/host/jabber:iq:register:query", handler, 10);
0649883de4d3 mod_password_policy: Initial commit.
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
   113
module:hook("stanza/iq/jabber:iq:register:query", handler, 10);