mod_auth_external/mod_auth_external.lua
changeset 1154 61f95bf51b35
parent 1086 50ee38e95e75
child 1155 40f7a8d152eb
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;