%%%------------------------------------------------------------------- %%% File : epam.erl %%% Author : Evgeniy Khramtsov %%% Purpose : PAM authentication and accounting management %%% Created : 5 Jul 2007 by Evgeniy Khramtsov %%% %%% %%% ejabberd, Copyright (C) 2002-2013 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License %%% along with this program; if not, write to the Free Software %%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA %%% 02111-1307 USA %%% %%%------------------------------------------------------------------- -module(epam). -author('xram@jabber.ru'). -behaviour(gen_server). -include_lib("kernel/include/file.hrl"). -include("ejabberd.hrl"). %% API -export([start_link/0, start/0, stop/0]). -export([authenticate/3, acct_mgmt/2]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -define(WARNING, "File ~p is world-wide executable. This " "is a possible security hole in your " "system. This file must be setted root " "on execution and only ejabberd must " "be able to read/execute it. You have " "been warned :)"). -define(PROCNAME, ?MODULE). -define(CMD_AUTH, 0). -define(CMD_ACCT, 1). -record(state, {port}). %%==================================================================== %% API %%==================================================================== start() -> ChildSpec = {?PROCNAME, {?MODULE, start_link, []}, transient, 1000, worker, [?MODULE]}, supervisor:start_child(ejabberd_sup, ChildSpec). stop() -> gen_server:call(?PROCNAME, stop), supervisor:terminate_child(ejabberd_sup, ?PROCNAME), supervisor:delete_child(ejabberd_sup, ?PROCNAME). start_link() -> gen_server:start_link({local, ?PROCNAME}, ?MODULE, [], []). authenticate(Srv, User, Pass) when is_binary(Srv), is_binary(User), is_binary(Pass) -> gen_server:call(?PROCNAME, {authenticate, Srv, User, Pass}). acct_mgmt(Srv, User) when is_binary(Srv), is_binary(User) -> gen_server:call(?PROCNAME, {acct_mgmt, Srv, User}). %%==================================================================== %% gen_server callbacks %%==================================================================== init([]) -> FileName = filename:join(ejabberd:get_bin_path(), "epam"), case file:read_file_info(FileName) of {ok, Info} -> Mode = Info#file_info.mode band 2049, if Mode == 2049 -> ?WARNING_MSG((?WARNING), [FileName]); true -> ok end, Port = open_port({spawn, FileName}, [{packet, 2}, binary, exit_status]), {ok, #state{port = Port}}; {error, Reason} -> ?ERROR_MSG("Can't open file ~p: ~p", [FileName, Reason]), error end. terminate(_Reason, #state{port = Port}) -> catch port_close(Port), ok. handle_call({authenticate, Srv, User, Pass}, From, State) -> Port = State#state.port, Data = term_to_binary({?CMD_AUTH, From, {Srv, User, Pass}}), port_command(Port, Data), {noreply, State}; handle_call({acct_mgmt, Srv, User}, From, State) -> Port = State#state.port, Data = term_to_binary({?CMD_ACCT, From, {Srv, User}}), port_command(Port, Data), {noreply, State}; handle_call(stop, _From, State) -> {stop, normal, ok, State}; handle_call(_Request, _From, State) -> {reply, bad_request, State}. handle_info({Port, {data, Data}}, #state{port = Port} = State) -> case binary_to_term(Data) of {Cmd, To, Reply} when Cmd == (?CMD_AUTH); Cmd == (?CMD_ACCT) -> gen_server:reply(To, Reply); Err -> ?ERROR_MSG("Got invalid reply from ~p: ~p", [Port, Err]) end, {noreply, State}; handle_info({Port, {exit_status, _}}, #state{port = Port} = State) -> {stop, port_died, State}; handle_info(Msg, State) -> ?WARNING_MSG("Got unexpected message: ~p", [Msg]), {noreply, State}. handle_cast(_Msg, State) -> {noreply, State}. code_change(_OldVsn, State, _Extra) -> {ok, State}.