comparison mod_auth_dovecot/mod_auth_dovecot.lua @ 268:cfcd4efb0fa4

mod_auth_dovecot: Remove asserts (use logger) and refactor socket code
author Javier Torres <javitonino@gmail.com>
date Sat, 30 Oct 2010 23:38:39 +0200
parents 76f3310ec113
children 74846ec9c29f
comparison
equal deleted inserted replaced
267:76f3310ec113 268:cfcd4efb0fa4
14 local pposix = require "util.pposix"; 14 local pposix = require "util.pposix";
15 15
16 local prosody = _G.prosody; 16 local prosody = _G.prosody;
17 17
18 function new_default_provider(host) 18 function new_default_provider(host)
19 local provider = { name = "dovecot" }; 19 local provider = { name = "dovecot", c = nil };
20 log("debug", "initializing dovecot authentication provider for host '%s'", host); 20 log("debug", "initializing dovecot authentication provider for host '%s'", host);
21 21
22 function provider.test_password(username, password) 22 -- The following connects to a new socket and send the handshake
23 log("debug", "test password '%s' for user %s at host %s", password, username, module.host); 23 function provider.connect(self)
24 -- Destroy old socket
25 if (provider.c ~= nil) then
26 provider.c:close();
27 end
24 28
25 c = assert(socket.unix()); 29 provider.c = socket.unix();
26 assert(c:connect("/var/run/dovecot/auth-login")); -- FIXME: Hardcoded is bad
27 30
31 -- Create a connection to dovecot socket
32 local socket = "/var/run/dovecot/auth-login";
33 local r, e = provider.c:connect(socket);
34 if (not r) then
35 log("warn", "error connecting to dovecot socket at '%s'. error was '%s'. check permissions", socket, e);
36 return false;
37 end
38
39 -- Send our handshake
28 local pid = pposix.getpid(); 40 local pid = pposix.getpid();
29 41 if not provider:send("VERSION\t1\t1\n") then
30 -- Send our handshake 42 return false
31 -- FIXME: Oh no! There are asserts everywhere 43 end
32 assert(c:send("VERSION\t1\t1\n")); 44 if (not provider:send("CPID\t" .. pid .. "\n")) then
33 assert(c:send("CPID\t" .. pid .. "\n")); 45 return false
34 46 end
35 -- Check their handshake 47
48 -- Parse Dovecot's handshake
36 local done = false; 49 local done = false;
37 while (not done) do 50 while (not done) do
38 local l = assert(c:receive()); 51 local l = provider:receive();
52 if (not l) then
53 return false;
54 end
55
39 parts = string.gmatch(l, "[^\t]+"); 56 parts = string.gmatch(l, "[^\t]+");
40 first = parts(); 57 first = parts();
41 if (first == "VERSION") then 58 if (first == "VERSION") then
42 assert(parts() == "1"); 59 -- Version should be 1.1
43 assert(parts() == "1"); 60 local v1 = parts();
61 local v2 = parts();
62
63 if (not (v1 == "1" and v2 == "1")) then
64 log("warn", "server version is not 1.1. it is %s.%s", v1, v2);
65 return false;
66 end
44 elseif (first == "MECH") then 67 elseif (first == "MECH") then
68 -- Mechanisms should include PLAIN
45 local ok = false; 69 local ok = false;
46 for p in parts do 70 for p in parts do
47 if p == "PLAIN" then 71 if p == "PLAIN" then
48 ok = true; 72 ok = true;
49 end 73 end
50 end 74 end
51 assert(ok); 75 if (not ok) then
76 log("warn", "server doesn't support PLAIN mechanism. It supports '%s'", l);
77 return false;
78 end
52 elseif (first == "DONE") then 79 elseif (first == "DONE") then
53 done = true; 80 done = true;
54 end 81 end
55 end 82 end
56 83 return true;
84 end
85
86 function provider.send(self, data)
87 local r, e = provider.c:send(data);
88 if (not r) then
89 log("warn", "error sending '%s' to dovecot. error was '%s'", data, e);
90 return false;
91 end
92 return true;
93 end
94
95 function provider.receive(self)
96 local r, e = provider.c:receive();
97 if (not r) then
98 log("warn", "error receiving data from dovecot. error was '%s'", socket, e);
99 return false;
100 end
101 return r;
102 end
103
104 function provider.test_password(username, password)
105 log("debug", "test password '%s' for user %s at host %s", password, username, module.host);
106
107 if (not provider:connect()) then
108 return nil, "Auth failed. Dovecot communications error";
109 end
110
57 -- Send auth data 111 -- Send auth data
58 username = username .. "@" .. module.host; -- FIXME: this is actually a hack for my server 112 username = username .. "@" .. module.host; -- FIXME: this is actually a hack for my server
59 local b64 = base64.encode(username .. "\0" .. username .. "\0" .. password); 113 local b64 = base64.encode(username .. "\0" .. username .. "\0" .. password);
60 local id = "54321"; -- FIXME: probably can just be a fixed value if making one request per connection 114 local id = "54321"; -- FIXME: probably can just be a fixed value if making one request per connection
61 assert(c:send("AUTH\t" .. id .. "\tPLAIN\tservice=XMPP\tresp=" .. b64 .. "\n")); 115 if (not provider:send("AUTH\t" .. id .. "\tPLAIN\tservice=XMPP\tresp=" .. b64 .. "\n")) then
62 local l = assert(c:receive()); 116 return nil, "Auth failed. Dovecot communications error";
63 assert(c:close()); 117 end
118
119 -- Get response
120 local l = provider:receive();
121 if (not l) then
122 return nil, "Auth failed. Dovecot communications error";
123 end
64 local parts = string.gmatch(l, "[^\t]+"); 124 local parts = string.gmatch(l, "[^\t]+");
65 125
126 -- Check response
66 if (parts() == "OK") then 127 if (parts() == "OK") then
67 return true; 128 return true;
68 else 129 else
69 return nil, "Auth failed. Invalid username or password."; 130 return nil, "Auth failed. Invalid username or password.";
70 end 131 end
71 end 132 end
72 133
73 function provider.get_password(username) 134 function provider.get_password(username)
74 return nil, "Cannot get_password in dovecot backend."; 135 return nil, "Cannot get_password in dovecot backend.";
75 end 136 end
76 137
77 function provider.set_password(username, password) 138 function provider.set_password(username, password)
78 return nil, "Cannot set_password in dovecot backend."; 139 return nil, "Cannot set_password in dovecot backend.";
79 end 140 end
80 141
81 function provider.user_exists(username) 142 function provider.user_exists(username)
82 --TODO: Send an auth request. If it returns FAIL <id> user=<user> then user exists. 143 --TODO: Send an auth request. If it returns FAIL <id> user=<user> then user exists.
83 return nil, "user_exists not yet implemented in dovecot backend."; 144 return nil, "user_exists not yet implemented in dovecot backend.";
84 end 145 end
85 146
86 function provider.create_user(username, password) 147 function provider.create_user(username, password)
87 return nil, "Cannot create_user in dovecot backend."; 148 return nil, "Cannot create_user in dovecot backend.";
88 end 149 end
89 150
90 function provider.get_sasl_handler() 151 function provider.get_sasl_handler()
91 local realm = module:get_option("sasl_realm") or module.host; 152 local realm = module:get_option("sasl_realm") or module.host;
92 local getpass_authentication_profile = { 153 local getpass_authentication_profile = {
93 plain_test = function(username, password, realm) 154 plain_test = function(username, password, realm)
94 local prepped_username = nodeprep(username); 155 local prepped_username = nodeprep(username);
95 if not prepped_username then 156 if not prepped_username then
96 log("debug", "NODEprep failed on username: %s", username); 157 log("debug", "NODEprep failed on username: %s", username);
97 return "", nil; 158 return "", nil;
98 end 159 end
99 return usermanager.test_password(prepped_username, realm, password), true; 160 return usermanager.test_password(prepped_username, realm, password), true;
100 end 161 end
101 }; 162 };
102 return new_sasl(realm, getpass_authentication_profile); 163 return new_sasl(realm, getpass_authentication_profile);
103 end 164 end
104 165
105 return provider; 166 return provider;
106 end 167 end
107 168
108 module:add_item("auth-provider", new_default_provider(module.host)); 169 module:add_item("auth-provider", new_default_provider(module.host));
109 170