Software /
code /
prosody-modules
Comparison
mod_auth_external/mod_auth_external.lua @ 1154:61f95bf51b35
mod_auth_external: Switch to lpty, remove file-based fallback, improve error messages and handling. Should greatly increase compatibility with scripts.
author | Matthew Wild <mwild1@gmail.com> |
---|---|
date | Tue, 13 Aug 2013 18:56:50 +0100 |
parent | 1086:50ee38e95e75 |
child | 1155:40f7a8d152eb |
comparison
equal
deleted
inserted
replaced
1153:572b1ad46182 | 1154:61f95bf51b35 |
---|---|
1 -- | |
2 -- NOTE: currently this uses lpc; when waqas fixes process, it can go back to that | |
3 -- | 1 -- |
4 -- Prosody IM | 2 -- Prosody IM |
5 -- Copyright (C) 2010 Waqas Hussain | 3 -- Copyright (C) 2010 Waqas Hussain |
6 -- Copyright (C) 2010 Jeff Mitchell | 4 -- Copyright (C) 2010 Jeff Mitchell |
7 -- Copyright (C) 2013 Mikael Nordfeldth | 5 -- Copyright (C) 2013 Mikael Nordfeldth |
6 -- Copyright (C) 2013 Matthew Wild, finally came to fix it all | |
8 -- | 7 -- |
9 -- This project is MIT/X11 licensed. Please see the | 8 -- This project is MIT/X11 licensed. Please see the |
10 -- COPYING file in the source package for more information. | 9 -- COPYING file in the source package for more information. |
11 -- | 10 -- |
12 | 11 |
12 local lpty = assert(require "lpty", "mod_auth_external requires lpty: https://code.google.com/p/prosody-modules/wiki/mod_auth_external#Installation"); | |
13 | 13 |
14 --local process = require "process"; | |
15 local lpc; pcall(function() lpc = require "lpc"; end); | |
16 | |
17 local config = require "core.configmanager"; | |
18 local log = module._log; | 14 local log = module._log; |
19 local host = module.host; | 15 local host = module.host; |
20 local script_type = config.get(host, "core", "external_auth_protocol") or "generic"; | 16 local script_type = module:get_option_string("external_auth_protocol", "generic"); |
21 assert(script_type == "ejabberd" or script_type == "generic"); | 17 assert(script_type == "ejabberd" or script_type == "generic", "Config error: external_auth_protocol must be 'ejabberd' or 'generic'"); |
22 local command = config.get(host, "core", "external_auth_command") or ""; | 18 local command = module:get_option_string("external_auth_command", ""); |
23 assert(type(command) == "string"); | 19 local read_timeout = module:get_option_number("external_auth_timeout", 5); |
24 assert(not host:find(":")); | 20 assert(not host:find(":"), "Invalid hostname"); |
25 local usermanager = require "core.usermanager"; | 21 local usermanager = require "core.usermanager"; |
26 local jid_bare = require "util.jid".bare; | 22 local jid_bare = require "util.jid".bare; |
27 local new_sasl = require "util.sasl".new; | 23 local new_sasl = require "util.sasl".new; |
28 | 24 |
29 local function send_query(text) | 25 local pty = lpty.new({ throw_errors = false, no_local_echo = true, use_path = false }); |
30 local tmpname = os.tmpname(); | |
31 local p = io.popen(command.." > "..tmpname, "w"); -- dump result to file | |
32 p:write(text); -- push colon-separated args through pipe to above command | |
33 p:close(); | |
34 local tmpfile = io.open(tmpname, "r"); -- open file to read auth result | |
35 local result; | |
36 if script_type == "ejabberd" then | |
37 result = tmpfile:read(4); | |
38 elseif script_type == "generic" then | |
39 result = tmpfile:read(); | |
40 end | |
41 tmpfile:close(); | |
42 os.remove(tmpname); -- clean up after us | |
43 return result; | |
44 end | |
45 | 26 |
46 if lpc then | 27 function send_query(text) |
47 --local proc; | 28 if not pty:hasproc() then |
48 local pid; | 29 local status, ret = pty:exitstatus(); |
49 local readfile; | 30 if status and (status ~= "exit" or ret ~= 0) then |
50 local writefile; | 31 log("warn", "Auth process exited unexpectedly with %s %d, restarting", status, ret or 0); |
51 | |
52 function send_query(text) | |
53 if pid and lpc.wait(pid,1) ~= nil then | |
54 log("debug","error, process died, force reopen"); | |
55 pid=nil; | |
56 end | |
57 if not pid then | |
58 log("debug", "Opening process " .. command); | |
59 -- proc = process.popen(command); | |
60 pid, writefile, readfile = lpc.run(command); | |
61 end | |
62 -- if not proc then | |
63 if not pid then | |
64 log("debug", "Process failed to open"); | |
65 return nil; | 32 return nil; |
66 end | 33 end |
67 -- proc:write(text); | 34 local ok, err = pty:startproc(command); |
68 -- proc:flush(); | 35 if not ok then |
36 log("error", "Failed to start auth process '%s': %s", command, err); | |
37 return nil; | |
38 end | |
39 log("debug", "Started auth process"); | |
40 end | |
69 | 41 |
70 writefile:write(text); | 42 pty:send(text); |
71 writefile:flush(); | 43 return pty:read(read_timeout); |
72 if script_type == "ejabberd" then | |
73 -- return proc:read(4); -- FIXME do properly | |
74 return readfile:read(4); -- FIXME do properly | |
75 elseif script_type == "generic" then | |
76 -- return proc:read(1); | |
77 return readfile:read(); | |
78 end | |
79 end | |
80 end | 44 end |
81 | 45 |
82 function do_query(kind, username, password) | 46 function do_query(kind, username, password) |
83 if not username then return nil, "not-acceptable"; end | 47 if not username then return nil, "not-acceptable"; end |
84 | 48 |
95 query = query..'\n'; | 59 query = query..'\n'; |
96 end | 60 end |
97 | 61 |
98 local response = send_query(query); | 62 local response = send_query(query); |
99 if (script_type == "ejabberd" and response == "\0\2\0\0") or | 63 if (script_type == "ejabberd" and response == "\0\2\0\0") or |
100 (script_type == "generic" and response == "0") then | 64 (script_type == "generic" and response:gsub("\r?\n$", "") == "0") then |
101 return nil, "not-authorized"; | 65 return nil, "not-authorized"; |
102 elseif (script_type == "ejabberd" and response == "\0\2\0\1") or | 66 elseif (script_type == "ejabberd" and response == "\0\2\0\1") or |
103 (script_type == "generic" and response == "1") then | 67 (script_type == "generic" and response:gsub("\r?\n$", "") == "1") then |
104 return true; | 68 return true; |
105 else | 69 else |
106 log("debug", "Nonsense back"); | 70 if response then |
107 --proc:close(); | 71 log("warn", "Unable to interpret data from auth process, %d bytes beginning with: %s", #response, (response:sub(1,4):gsub(".", function (c) |
108 --proc = nil; | 72 return ("%02X "):format(c:byte()); |
73 end))); | |
74 else | |
75 log("warn", "Error while waiting for result from auth process: %s", response or "unknown error"); | |
76 end | |
109 return nil, "internal-server-error"; | 77 return nil, "internal-server-error"; |
110 end | 78 end |
111 end | 79 end |
112 | 80 |
113 local host = module.host; | 81 local host = module.host; |