Mercurial > prosody-modules
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)); |