1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
-- mod_auth_ldap
local jid_split = require "util.jid".split;
local new_sasl = require "util.sasl".new;
local lualdap = require "lualdap";
local function ldap_filter_escape(s)
return (s:gsub("[*()\\%z]", function(c) return ("\\%02x"):format(c:byte()) end));
end
-- Config options
local ldap_server = module:get_option_string("ldap_server", "localhost");
local ldap_rootdn = module:get_option_string("ldap_rootdn", "");
local ldap_password = module:get_option_string("ldap_password", "");
local ldap_tls = module:get_option_boolean("ldap_tls");
local ldap_scope = module:get_option_string("ldap_scope", "subtree");
local ldap_filter = module:get_option_string("ldap_filter", "(uid=$user)"):gsub("%%s", "$user", 1);
local ldap_base = assert(module:get_option_string("ldap_base"), "ldap_base is a required option for ldap");
local ldap_mode = module:get_option_string("ldap_mode", "bind");
local ldap_admins = module:get_option_string("ldap_admin_filter",
module:get_option_string("ldap_admins")); -- COMPAT with mistake in documentation
local host = ldap_filter_escape(module:get_option_string("realm", module.host));
-- Initiate connection
local ld = nil;
module.unload = function() if ld then pcall(ld, ld.close); end end
function ldap_do_once(method, ...)
if ld == nil then
local err;
ld, err = lualdap.open_simple(ldap_server, ldap_rootdn, ldap_password, ldap_tls);
if not ld then return nil, err, "reconnect"; end
end
-- luacheck: ignore 411/success
local success, iterator, invariant, initial = pcall(ld[method], ld, ...);
if not success then ld = nil; return nil, iterator, "search"; end
local success, dn, attr = pcall(iterator, invariant, initial);
if not success then ld = nil; return success, dn, "iter"; end
return dn, attr, "return";
end
function ldap_do(method, retry_count, ...)
local dn, attr, where;
for _=1,1+retry_count do
dn, attr, where = ldap_do_once(method, ...);
if dn or not(attr) then break; end -- nothing or something found
module:log("warn", "LDAP: %s %s (in %s)", tostring(dn), tostring(attr), where);
-- otherwise retry
end
if not dn and attr then
module:log("error", "LDAP: %s", tostring(attr));
end
return dn, attr;
end
function get_user(username)
module:log("debug", "get_user(%q)", username);
return ldap_do("search", 2, {
base = ldap_base;
scope = ldap_scope;
sizelimit = 1;
filter = ldap_filter:gsub("%$(%a+)", {
user = ldap_filter_escape(username);
host = host;
});
});
end
local provider = {};
function provider.create_user(username, password) -- luacheck: ignore 212
return nil, "Account creation not available with LDAP.";
end
function provider.user_exists(username)
return not not get_user(username);
end
function provider.set_password(username, password)
local dn, attr = get_user(username);
if not dn then return nil, attr end
if attr.userPassword == password then return true end
return ldap_do("modify", 2, dn, { '=', userPassword = password });
end
if ldap_mode == "getpasswd" then
function provider.get_password(username)
local dn, attr = get_user(username);
if dn and attr then
return attr.userPassword;
end
end
function provider.test_password(username, password)
return provider.get_password(username) == password;
end
function provider.get_sasl_handler()
return new_sasl(module.host, {
plain = function(sasl, username) -- luacheck: ignore 212/sasl
local password = provider.get_password(username);
if not password then return "", nil; end
return password, true;
end
});
end
elseif ldap_mode == "bind" then
local function test_password(userdn, password)
local ok, err = lualdap.open_simple(ldap_server, userdn, password, ldap_tls);
if not ok then
module:log("debug", "ldap open_simple error: %s", err);
end
return not not ok;
end
function provider.test_password(username, password)
local dn = get_user(username);
if not dn then return end
return test_password(dn, password)
end
function provider.get_sasl_handler()
return new_sasl(module.host, {
plain_test = function(sasl, username, password) -- luacheck: ignore 212/sasl
return provider.test_password(username, password), true;
end
});
end
else
module:log("error", "Unsupported ldap_mode %s", tostring(ldap_mode));
end
if ldap_admins then
function provider.is_admin(jid)
local username, user_host = jid_split(jid);
if user_host ~= module.host then
return false;
end
return ldap_do("search", 2, {
base = ldap_base;
scope = ldap_scope;
sizelimit = 1;
filter = ldap_admins:gsub("%$(%a+)", {
user = ldap_filter_escape(username);
host = host;
});
});
end
end
module:provides("auth", provider);
|