comparison mod_auth_dovecot/mod_auth_dovecot.lua @ 312:31deafcbf623

mod_auth_dovecot: Fix various global variable sets/gets, log levels, unclear variable names and change coding style to match more closely the rest of the Prosody code.
author Matthew Wild <mwild1@gmail.com>
date Sat, 08 Jan 2011 17:57:03 +0000
parents f663ea45436f
children 6560fd0b77f5
comparison
equal deleted inserted replaced
311:f663ea45436f 312:31deafcbf623
5 -- Copyright (C) 2008-2010 Waqas Hussain 5 -- Copyright (C) 2008-2010 Waqas Hussain
6 -- 6 --
7 7
8 local socket_unix = require "socket.unix"; 8 local socket_unix = require "socket.unix";
9 local datamanager = require "util.datamanager"; 9 local datamanager = require "util.datamanager";
10 local usermanager = require "core.usermanager";
10 local log = require "util.logger".init("auth_dovecot"); 11 local log = require "util.logger".init("auth_dovecot");
11 local new_sasl = require "util.sasl".new; 12 local new_sasl = require "util.sasl".new;
12 local nodeprep = require "util.encodings".stringprep.nodeprep; 13 local nodeprep = require "util.encodings".stringprep.nodeprep;
13 local base64 = require "util.encodings".base64; 14 local base64 = require "util.encodings".base64;
14 local pposix = require "util.pposix"; 15 local pposix = require "util.pposix";
15 16
16 local prosody = _G.prosody; 17 local prosody = prosody;
17 local socket_path = module:get_option_string("dovecot_auth_socket", "/var/run/dovecot/auth-login"); 18 local socket_path = module:get_option_string("dovecot_auth_socket", "/var/run/dovecot/auth-login");
18 19
19 function new_default_provider(host) 20 function new_provider(host)
20 local provider = { name = "dovecot", request_id = 0 }; 21 local provider = { name = "dovecot", request_id = 0 };
21 log("debug", "initializing dovecot authentication provider for host '%s'", host); 22 log("debug", "initializing dovecot authentication provider for host '%s'", host);
22 23
23 local conn; 24 local conn;
24 25
37 38
38 conn = socket.unix(); 39 conn = socket.unix();
39 40
40 -- Create a connection to dovecot socket 41 -- Create a connection to dovecot socket
41 log("debug", "connecting to dovecot socket at '%s'", socket_path); 42 log("debug", "connecting to dovecot socket at '%s'", socket_path);
42 local r, e = conn:connect(socket_path); 43 local ok, err = conn:connect(socket_path);
43 if (not r) then 44 if not ok then
44 log("warn", "error connecting to dovecot socket at '%s'. error was '%s'. check permissions", socket_path, e); 45 log("error", "error connecting to dovecot socket at '%s'. error was '%s'. check permissions", socket_path, err);
45 provider:close(); 46 provider:close();
46 return false; 47 return false;
47 end 48 end
48 49
49 -- Send our handshake 50 -- Send our handshake
50 local pid = pposix.getpid(); 51 local pid = pposix.getpid();
51 log("debug", "sending handshake to dovecot. version 1.1, cpid '%d'", pid); 52 log("debug", "sending handshake to dovecot. version 1.1, cpid '%d'", pid);
52 if not provider:send("VERSION\t1\t1\n") then 53 if not provider:send("VERSION\t1\t1\n") then
53 return false 54 return false
54 end 55 end
55 if (not provider:send("CPID\t" .. pid .. "\n")) then 56 if not provider:send("CPID\t" .. pid .. "\n") then
56 return false 57 return false
57 end 58 end
58 59
59 -- Parse Dovecot's handshake 60 -- Parse Dovecot's handshake
60 local done = false; 61 local done = false;
61 while (not done) do 62 while (not done) do
62 local l = provider:receive(); 63 local line = provider:receive();
63 if (not l) then 64 if not line then
64 return false; 65 return false;
65 end 66 end
66 67
67 log("debug", "dovecot handshake: '%s'", l); 68 log("debug", "dovecot handshake: '%s'", line);
68 parts = string.gmatch(l, "[^\t]+"); 69 local parts = line:gmatch("[^\t]+");
69 first = parts(); 70 local first = parts();
70 if (first == "VERSION") then 71 if first == "VERSION" then
71 -- Version should be 1.1 72 -- Version should be 1.1
72 local major_version = parts(); 73 local major_version = parts();
73 74
74 if major_version ~= "1" then 75 if major_version ~= "1" then
75 log("error", "dovecot server version is not 1.x. it is %s.x", major_version); 76 log("error", "dovecot server version is not 1.x. it is %s.x", major_version);
76 provider:close(); 77 provider:close();
77 return false; 78 return false;
78 end 79 end
79 elseif (first == "MECH") then 80 elseif first == "MECH" then
80 -- Mechanisms should include PLAIN 81 -- Mechanisms should include PLAIN
81 local ok = false; 82 local ok = false;
82 for p in parts do 83 for part in parts do
83 if p == "PLAIN" then 84 if part == "PLAIN" then
84 ok = true; 85 ok = true;
85 end 86 end
86 end 87 end
87 if (not ok) then 88 if not ok then
88 log("warn", "server doesn't support PLAIN mechanism. It supports '%s'", l); 89 log("warn", "server doesn't support PLAIN mechanism. It supports '%s'", line);
89 provider:close(); 90 provider:close();
90 return false; 91 return false;
91 end 92 end
92 elseif (first == "DONE") then 93 elseif first == "DONE" then
93 done = true; 94 done = true;
94 end 95 end
95 end 96 end
96 return true; 97 return true;
97 end 98 end
98 99
99 -- Wrapper for send(). Handles errors 100 -- Wrapper for send(). Handles errors
100 function provider.send(self, data) 101 function provider.send(self, data)
101 local r, e = conn:send(data); 102 local ok, err = conn:send(data);
102 if (not r) then 103 if not ok then
103 log("warn", "error sending '%s' to dovecot. error was '%s'", data, e); 104 log("error", "error sending '%s' to dovecot. error was '%s'", data, err);
104 provider:close(); 105 provider:close();
105 return false; 106 return false;
106 end 107 end
107 return true; 108 return true;
108 end 109 end
109 110
110 -- Wrapper for receive(). Handles errors 111 -- Wrapper for receive(). Handles errors
111 function provider.receive(self) 112 function provider.receive(self)
112 local r, e = conn:receive(); 113 local line, err = conn:receive();
113 if (not r) then 114 if not line then
114 log("warn", "error receiving data from dovecot. error was '%s'", socket, e); 115 log("error", "error receiving data from dovecot. error was '%s'", err);
115 provider:close(); 116 provider:close();
116 return false; 117 return false;
117 end 118 end
118 return r; 119 return line;
119 end 120 end
120 121
121 function provider.send_auth_request(self, username, password) 122 function provider.send_auth_request(self, username, password)
122 if not conn then 123 if not conn then
123 if not provider:connect() then 124 if not provider:connect() then
130 local b64 = base64.encode(username .. "\0" .. username .. "\0" .. password); 131 local b64 = base64.encode(username .. "\0" .. username .. "\0" .. password);
131 provider.request_id = provider.request_id + 1 % 4294967296 132 provider.request_id = provider.request_id + 1 % 4294967296
132 133
133 local msg = "AUTH\t" .. provider.request_id .. "\tPLAIN\tservice=XMPP\tresp=" .. b64; 134 local msg = "AUTH\t" .. provider.request_id .. "\tPLAIN\tservice=XMPP\tresp=" .. b64;
134 log("debug", "sending auth request for '%s' with password '%s': '%s'", username, password, msg); 135 log("debug", "sending auth request for '%s' with password '%s': '%s'", username, password, msg);
135 if (not provider:send(msg .. "\n")) then 136 if not provider:send(msg .. "\n") then
136 return nil, "Auth failed. Dovecot communications error"; 137 return nil, "Auth failed. Dovecot communications error";
137 end 138 end
138 139
139 140
140 -- Get response 141 -- Get response
141 local l = provider:receive(); 142 local line = provider:receive();
142 log("debug", "got auth response: '%s'", l); 143 log("debug", "got auth response: '%s'", line);
143 if (not l) then 144 if not line then
144 return nil, "Auth failed. Dovecot communications error"; 145 return nil, "Auth failed. Dovecot communications error";
145 end 146 end
146 local parts = string.gmatch(l, "[^\t]+"); 147 local parts = line:gmatch("[^\t]+");
147 148
148 -- Check response 149 -- Check response
149 local status = parts(); 150 local status = parts();
150 local resp_id = tonumber(parts()); 151 local resp_id = tonumber(parts());
151 152
152 if (resp_id ~= provider.request_id) then 153 if resp_id ~= provider.request_id then
153 log("warn", "dovecot response_id(%s) doesn't match request_id(%s)", resp_id, provider.request_id); 154 log("warn", "dovecot response_id(%s) doesn't match request_id(%s)", resp_id, provider.request_id);
154 provider:close(); 155 provider:close();
155 return nil, "Auth failed. Dovecot communications error"; 156 return nil, "Auth failed. Dovecot communications error";
156 end 157 end
157 158
161 function provider.test_password(username, password) 162 function provider.test_password(username, password)
162 log("debug", "test password '%s' for user %s at host %s", password, username, module.host); 163 log("debug", "test password '%s' for user %s at host %s", password, username, module.host);
163 164
164 local status, extra = provider:send_auth_request(username, password); 165 local status, extra = provider:send_auth_request(username, password);
165 166
166 if (status == "OK") then 167 if status == "OK" then
167 log("info", "login ok for '%s'", username); 168 log("info", "login ok for '%s'", username);
168 return true; 169 return true;
169 else 170 else
170 log("info", "login failed for '%s'", username); 171 log("info", "login failed for '%s'", username);
171 return nil, "Auth failed. Invalid username or password."; 172 return nil, "Auth failed. Invalid username or password.";
186 -- Send a request. If the response (FAIL) contains an extra 187 -- Send a request. If the response (FAIL) contains an extra
187 -- parameter like user=<username> then it exists. 188 -- parameter like user=<username> then it exists.
188 local status, extra = provider:send_auth_request(username, ""); 189 local status, extra = provider:send_auth_request(username, "");
189 190
190 local param = extra(); 191 local param = extra();
191 while (param) do 192 while param do
192 parts = string.gmatch(param, "[^=]+"); 193 local parts = param:gmatch("[^=]+");
193 name = parts(); 194 local name = parts();
194 value = parts(); 195 local value = parts();
195 if (name == "user") then 196 if name == "user" then
196 log("info", "user '%s' exists", username); 197 log("debug", "user '%s' exists", username);
197 return true; 198 return true;
198 end 199 end
199 200
200 param = extra(); 201 param = extra();
201 end 202 end
202 203
203 log("info", "user '%s' does not exists (or dovecot didn't send user=<username> parameter)", username); 204 log("debug", "user '%s' does not exists (or dovecot didn't send user=<username> parameter)", username);
204 return false; 205 return false;
205 end 206 end
206 207
207 function provider.create_user(username, password) 208 function provider.create_user(username, password)
208 return nil, "Cannot create_user in dovecot backend."; 209 return nil, "Cannot create_user in dovecot backend.";
224 end 225 end
225 226
226 return provider; 227 return provider;
227 end 228 end
228 229
229 module:add_item("auth-provider", new_default_provider(module.host)); 230 module:add_item("auth-provider", new_provider(module.host));