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