mirror of
https://github.com/processone/ejabberd.git
synced 2025-01-01 17:53:00 +01:00
SVN Revision: 954
This commit is contained in:
parent
63487ff7b2
commit
4971de9d2a
88
src/ejabberd_auth_pam.erl
Normal file
88
src/ejabberd_auth_pam.erl
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% File : ejabberd_auth_pam.erl
|
||||||
|
%%% Author : Evgeniy Khramtsov <xram@jabber.ru>
|
||||||
|
%%% Purpose : PAM authentication
|
||||||
|
%%% Created : 5 Jul 2007 by Evgeniy Khramtsov <xram@jabber.ru>
|
||||||
|
%%% Id : $Id$
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(ejabberd_auth_pam).
|
||||||
|
-author('xram@jabber.ru').
|
||||||
|
|
||||||
|
%% External exports
|
||||||
|
-export([start/1,
|
||||||
|
set_password/3,
|
||||||
|
check_password/3,
|
||||||
|
check_password/5,
|
||||||
|
try_register/3,
|
||||||
|
dirty_get_registered_users/0,
|
||||||
|
get_vh_registered_users/1,
|
||||||
|
get_password/2,
|
||||||
|
get_password_s/2,
|
||||||
|
is_user_exists/2,
|
||||||
|
remove_user/2,
|
||||||
|
remove_user/3,
|
||||||
|
plain_password_required/0
|
||||||
|
]).
|
||||||
|
|
||||||
|
%%====================================================================
|
||||||
|
%% API
|
||||||
|
%%====================================================================
|
||||||
|
start(_Host) ->
|
||||||
|
case epam:start() of
|
||||||
|
{ok, _} -> ok;
|
||||||
|
{error,{already_started, _}} -> ok;
|
||||||
|
Err -> Err
|
||||||
|
end.
|
||||||
|
|
||||||
|
set_password(_User, _Server, _Password) ->
|
||||||
|
{error, not_allowed}.
|
||||||
|
|
||||||
|
check_password(User, Server, Password, _StreamID, _Digest) ->
|
||||||
|
check_password(User, Server, Password).
|
||||||
|
|
||||||
|
check_password(User, Host, Password) ->
|
||||||
|
Service = get_pam_service(Host),
|
||||||
|
case catch epam:authenticate(Service, User, Password) of
|
||||||
|
true -> true;
|
||||||
|
_ -> false
|
||||||
|
end.
|
||||||
|
|
||||||
|
try_register(_User, _Server, _Password) ->
|
||||||
|
{error, not_allowed}.
|
||||||
|
|
||||||
|
dirty_get_registered_users() ->
|
||||||
|
[].
|
||||||
|
|
||||||
|
get_vh_registered_users(_Host) ->
|
||||||
|
[].
|
||||||
|
|
||||||
|
get_password(_User, _Server) ->
|
||||||
|
false.
|
||||||
|
|
||||||
|
get_password_s(_User, _Server) ->
|
||||||
|
"".
|
||||||
|
|
||||||
|
is_user_exists(User, Host) ->
|
||||||
|
Service = get_pam_service(Host),
|
||||||
|
case catch epam:acct_mgmt(Service, User) of
|
||||||
|
true -> true;
|
||||||
|
_ -> false
|
||||||
|
end.
|
||||||
|
|
||||||
|
remove_user(_User, _Server) ->
|
||||||
|
{error, not_allowed}.
|
||||||
|
|
||||||
|
remove_user(_User, _Server, _Password) ->
|
||||||
|
{error, not_allowed}.
|
||||||
|
|
||||||
|
plain_password_required() ->
|
||||||
|
true.
|
||||||
|
|
||||||
|
%%====================================================================
|
||||||
|
%% Internal functions
|
||||||
|
%%====================================================================
|
||||||
|
get_pam_service(Host) ->
|
||||||
|
case ejabberd_config:get_local_option({pam_service, Host}) of
|
||||||
|
undefined -> "ejabberd";
|
||||||
|
Service -> Service
|
||||||
|
end.
|
43
src/pam/Makefile.in
Normal file
43
src/pam/Makefile.in
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# $Id: Makefile.in 775 2007-05-29 14:31:12Z mremond $
|
||||||
|
|
||||||
|
CC = @CC@
|
||||||
|
CFLAGS = @CFLAGS@ @PAM_CFLAGS@ @ERLANG_CFLAGS@
|
||||||
|
CPPFLAGS = @CPPFLAGS@
|
||||||
|
LDFLAGS = @LDFLAGS@
|
||||||
|
LIBS = @LIBS@ @PAM_LIBS@ @ERLANG_LIBS@
|
||||||
|
|
||||||
|
SUBDIRS =
|
||||||
|
|
||||||
|
ERLSHLIBS = ../epam
|
||||||
|
|
||||||
|
OUTDIR = ..
|
||||||
|
EFLAGS = -I .. -pz ..
|
||||||
|
# make debug=true to compile Erlang module with debug informations.
|
||||||
|
ifdef debug
|
||||||
|
EFLAGS+=+debug_info
|
||||||
|
endif
|
||||||
|
|
||||||
|
OBJS = \
|
||||||
|
$(OUTDIR)/epam.beam
|
||||||
|
|
||||||
|
all: $(OBJS) $(ERLSHLIBS)
|
||||||
|
|
||||||
|
$(OUTDIR)/%.beam: %.erl
|
||||||
|
@ERLC@ -W $(EFLAGS) -o $(OUTDIR) $<
|
||||||
|
|
||||||
|
#all: $(ERLSHLIBS)
|
||||||
|
# erl -s make all report "{outdir, \"..\"}" -noinput -s erlang halt
|
||||||
|
|
||||||
|
$(ERLSHLIBS): ../%: %.c
|
||||||
|
$(CC) -Wall $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) \
|
||||||
|
$(subst ../,,$(subst ,.c,$@)) $(LIBS) \
|
||||||
|
-o $@ -lpthread
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(OBJS) $(ERLSHLIBS)
|
||||||
|
|
||||||
|
distclean: clean
|
||||||
|
rm -f Makefile
|
||||||
|
|
||||||
|
TAGS:
|
||||||
|
etags *.erl
|
250
src/pam/epam.c
Normal file
250
src/pam/epam.c
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
#include <security/pam_appl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <erl_interface.h>
|
||||||
|
#include <ei.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define dec_int16(s) ((((unsigned char*) (s))[0] << 8) | \
|
||||||
|
(((unsigned char*) (s))[1]))
|
||||||
|
|
||||||
|
#define enc_int16(i, s) {((unsigned char*)(s))[0] = ((i) >> 8) & 0xff; \
|
||||||
|
((unsigned char*)(s))[1] = (i) & 0xff;}
|
||||||
|
|
||||||
|
#define BUFSIZE (1 << 16)
|
||||||
|
#define CMD_AUTH 0
|
||||||
|
#define CMD_ACCT 1
|
||||||
|
|
||||||
|
typedef unsigned char byte;
|
||||||
|
|
||||||
|
#ifdef PAM_FAIL_DELAY
|
||||||
|
static void delay_fn(int retval, unsigned usec_delay, void *appdata_ptr)
|
||||||
|
{
|
||||||
|
/* No delay. However, looks like some PAM modules ignore this */
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int misc_conv(int num_msg,
|
||||||
|
const struct pam_message **msg,
|
||||||
|
struct pam_response **resp,
|
||||||
|
void *password)
|
||||||
|
{
|
||||||
|
int msg_style;
|
||||||
|
if (num_msg != 1)
|
||||||
|
return PAM_CONV_ERR;
|
||||||
|
msg_style = msg[0]->msg_style;
|
||||||
|
if ((msg_style != PAM_PROMPT_ECHO_OFF) && (msg_style != PAM_PROMPT_ECHO_ON))
|
||||||
|
return PAM_CONV_ERR;
|
||||||
|
*resp = malloc(sizeof(struct pam_response));
|
||||||
|
(*resp)[0].resp_retcode = 0;
|
||||||
|
(*resp)[0].resp = strdup(password);
|
||||||
|
return PAM_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int auth(char *service, char *user, char *password)
|
||||||
|
{
|
||||||
|
struct pam_conv conv = {misc_conv, password};
|
||||||
|
int retval;
|
||||||
|
pam_handle_t *pamh = NULL;
|
||||||
|
retval = pam_start(service, user, &conv, &pamh);
|
||||||
|
if (retval == PAM_SUCCESS)
|
||||||
|
retval = pam_set_item(pamh, PAM_RUSER, user);
|
||||||
|
#ifdef PAM_FAIL_DELAY
|
||||||
|
if (retval == PAM_SUCCESS)
|
||||||
|
retval = pam_set_item(pamh, PAM_FAIL_DELAY, (void *)delay_fn);
|
||||||
|
#endif
|
||||||
|
if (retval == PAM_SUCCESS)
|
||||||
|
retval = pam_authenticate(pamh, 0);
|
||||||
|
if (retval == PAM_SUCCESS)
|
||||||
|
retval = pam_acct_mgmt(pamh, 0);
|
||||||
|
pam_end(pamh, retval);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int acct_mgmt(char *service, char *user)
|
||||||
|
{
|
||||||
|
struct pam_conv conv = {misc_conv, NULL};
|
||||||
|
int retval;
|
||||||
|
pam_handle_t *pamh = NULL;
|
||||||
|
retval = pam_start(service, user, &conv, &pamh);
|
||||||
|
if (retval == PAM_SUCCESS)
|
||||||
|
retval = pam_set_item(pamh, PAM_RUSER, user);
|
||||||
|
#ifdef PAM_FAIL_DELAY
|
||||||
|
if (retval == PAM_SUCCESS)
|
||||||
|
retval = pam_set_item(pamh, PAM_FAIL_DELAY, (void *)delay_fn);
|
||||||
|
#endif
|
||||||
|
if (retval == PAM_SUCCESS)
|
||||||
|
retval = pam_acct_mgmt(pamh, 0);
|
||||||
|
pam_end(pamh, retval);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int read_buf(int fd, byte *buf, int len)
|
||||||
|
{
|
||||||
|
int i, got = 0;
|
||||||
|
do {
|
||||||
|
if ((i = read(fd, buf+got, len-got)) <= 0) {
|
||||||
|
if (i == 0) return got;
|
||||||
|
if (errno != EINTR)
|
||||||
|
return got;
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
got += i;
|
||||||
|
} while (got < len);
|
||||||
|
return (len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int read_cmd(byte *buf)
|
||||||
|
{
|
||||||
|
int len;
|
||||||
|
if (read_buf(0, buf, 2) != 2)
|
||||||
|
return 0;
|
||||||
|
len = dec_int16(buf);
|
||||||
|
if (read_buf(0, buf, len) != len)
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int write_buf(int fd, char *buf, int len)
|
||||||
|
{
|
||||||
|
int i, done = 0;
|
||||||
|
do {
|
||||||
|
if ((i = write(fd, buf+done, len-done)) < 0) {
|
||||||
|
if (errno != EINTR)
|
||||||
|
return (i);
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
done += i;
|
||||||
|
} while (done < len);
|
||||||
|
return (len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int write_cmd(char *buf, int len)
|
||||||
|
{
|
||||||
|
byte hd[2];
|
||||||
|
enc_int16(len, hd);
|
||||||
|
if (write_buf(1, hd, 2) != 2)
|
||||||
|
return 0;
|
||||||
|
if (write_buf(1, buf, len) != len)
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int process_reply(ETERM *pid, int cmd, int res)
|
||||||
|
{
|
||||||
|
ETERM *result;
|
||||||
|
int len, retval;
|
||||||
|
const char *errtxt;
|
||||||
|
byte *buf;
|
||||||
|
if (res == PAM_SUCCESS)
|
||||||
|
result = erl_format("{~i, ~w, true}", cmd, pid);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
errtxt = pam_strerror(NULL, res);
|
||||||
|
result = erl_format("{~i, ~w, {false, ~s}}", cmd, pid, errtxt);
|
||||||
|
}
|
||||||
|
len = erl_term_len(result);
|
||||||
|
buf = erl_malloc(len);
|
||||||
|
erl_encode(result, buf);
|
||||||
|
retval = write_cmd(buf, len);
|
||||||
|
erl_free_term(result);
|
||||||
|
erl_free(buf);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int process_acct(ETERM *pid, ETERM *data)
|
||||||
|
{
|
||||||
|
int retval = 0;
|
||||||
|
ETERM *pattern, *srv, *user;
|
||||||
|
char *service, *username;
|
||||||
|
pattern = erl_format("{Srv, User}");
|
||||||
|
if (erl_match(pattern, data))
|
||||||
|
{
|
||||||
|
srv = erl_var_content(pattern, "Srv");
|
||||||
|
service = erl_iolist_to_string(srv);
|
||||||
|
user = erl_var_content(pattern, "User");
|
||||||
|
username = erl_iolist_to_string(user);
|
||||||
|
retval = process_reply(pid, CMD_ACCT, acct_mgmt(service, username));
|
||||||
|
erl_free_term(srv);
|
||||||
|
erl_free_term(user);
|
||||||
|
erl_free(service);
|
||||||
|
erl_free(username);
|
||||||
|
}
|
||||||
|
erl_free_term(pattern);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int process_auth(ETERM *pid, ETERM *data)
|
||||||
|
{
|
||||||
|
int retval = 0;
|
||||||
|
ETERM *pattern, *srv, *user, *pass;
|
||||||
|
char *service, *username, *password;
|
||||||
|
pattern = erl_format("{Srv, User, Pass}");
|
||||||
|
if (erl_match(pattern, data))
|
||||||
|
{
|
||||||
|
srv = erl_var_content(pattern, "Srv");
|
||||||
|
service = erl_iolist_to_string(srv);
|
||||||
|
user = erl_var_content(pattern, "User");
|
||||||
|
username = erl_iolist_to_string(user);
|
||||||
|
pass = erl_var_content(pattern, "Pass");
|
||||||
|
password = erl_iolist_to_string(pass);
|
||||||
|
retval = process_reply(pid, CMD_AUTH, auth(service, username, password));
|
||||||
|
erl_free_term(srv);
|
||||||
|
erl_free_term(user);
|
||||||
|
erl_free_term(pass);
|
||||||
|
erl_free(service);
|
||||||
|
erl_free(username);
|
||||||
|
erl_free(password);
|
||||||
|
};
|
||||||
|
erl_free_term(pattern);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int process_command(byte *buf)
|
||||||
|
{
|
||||||
|
int retval = 0;
|
||||||
|
ETERM *pattern, *tuple, *cmd, *port, *data;
|
||||||
|
pattern = erl_format("{Cmd, Port, Data}");
|
||||||
|
tuple = erl_decode(buf);
|
||||||
|
if (erl_match(pattern, tuple))
|
||||||
|
{
|
||||||
|
cmd = erl_var_content(pattern, "Cmd");
|
||||||
|
port = erl_var_content(pattern, "Port");
|
||||||
|
data = erl_var_content(pattern, "Data");
|
||||||
|
switch (ERL_INT_VALUE(cmd))
|
||||||
|
{
|
||||||
|
case CMD_AUTH:
|
||||||
|
retval = process_auth(port, data);
|
||||||
|
break;
|
||||||
|
case CMD_ACCT:
|
||||||
|
retval = process_acct(port, data);
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
erl_free_term(cmd);
|
||||||
|
erl_free_term(port);
|
||||||
|
erl_free_term(data);
|
||||||
|
}
|
||||||
|
erl_free_term(pattern);
|
||||||
|
erl_free_term(tuple);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void loop(void)
|
||||||
|
{
|
||||||
|
byte buf[BUFSIZE];
|
||||||
|
int retval = 0;
|
||||||
|
do {
|
||||||
|
if (read_cmd(buf) > 0)
|
||||||
|
retval = process_command(buf);
|
||||||
|
else
|
||||||
|
retval = 0;
|
||||||
|
} while (retval);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
erl_init(NULL, 0);
|
||||||
|
loop();
|
||||||
|
return 0;
|
||||||
|
}
|
117
src/pam/epam.erl
Normal file
117
src/pam/epam.erl
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% File : epam.erl
|
||||||
|
%%% Author : Evgeniy Khramtsov <xram@jabber.ru>
|
||||||
|
%%% Purpose : PAM authentication and accounting management
|
||||||
|
%%% Created : 5 Jul 2007 by Evgeniy Khramtsov <xram@jabber.ru>
|
||||||
|
%%% Id : $Id$
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-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_list(Srv), is_list(User), is_list(Pass) ->
|
||||||
|
gen_server:call(?PROCNAME, {authenticate, Srv, User, Pass}).
|
||||||
|
|
||||||
|
acct_mgmt(Srv, User) when is_list(Srv), is_list(User) ->
|
||||||
|
gen_server:call(?PROCNAME, {acct_mgmt, Srv, User}).
|
||||||
|
|
||||||
|
%%====================================================================
|
||||||
|
%% gen_server callbacks
|
||||||
|
%%====================================================================
|
||||||
|
init([]) ->
|
||||||
|
FileName = filename:join(ejabberd:get_so_path(), "epam"),
|
||||||
|
case file:read_file_info(FileName) of
|
||||||
|
{ok, Info} ->
|
||||||
|
Mode = Info#file_info.mode band 16#801,
|
||||||
|
if Mode == 16#801 ->
|
||||||
|
?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) ->
|
||||||
|
%% We can restart the port here, but, I think, it is not a good idea,
|
||||||
|
%% since we can run into infinity loop. So let the supervisor restart us.
|
||||||
|
{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}.
|
Loading…
Reference in New Issue
Block a user