Commit c749eee2 authored by Philipp Huebner's avatar Philipp Huebner

Imported Upstream version 0.2013.05.15

parents
all: src
src:
rebar compile
clean:
rebar clean
.PHONY: clean src
/*
* epam, 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
*
*/
#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, (char *)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;
ETERM *errbin;
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);
errbin = erl_mk_binary(errtxt, strlen(errtxt));
result = erl_format("{~i, ~w, {false, ~w}}", cmd, pid, errbin);
erl_free_term(errbin);
}
len = erl_term_len(result);
buf = erl_malloc(len);
erl_encode(result, buf);
retval = write_cmd((char *)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;
}
This source diff could not be displayed because it is too large. You can view the blob instead.
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.53)
AC_PACKAGE_VERSION(0.1.0)
AC_INIT(epam, 0.1.0, [], [])
# Checks for programs.
AC_PROG_CC
AC_PROG_MAKE_SET
if test "x$GCC" = "xyes"; then
CFLAGS="$CFLAGS -Wall"
fi
# Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
# Checks for library functions.
AC_FUNC_MALLOC
AC_HEADER_STDC
# Checks Erlang runtime and compiler
AC_ERLANG_NEED_ERL
AC_ERLANG_NEED_ERLC
# Checks and sets ERLANG_ROOT_DIR and ERLANG_LIB_DIR variable
# AC_ERLANG_SUBST_ROOT_DIR
# AC_ERLANG_SUBST_LIB_DIR
AC_CHECK_LIB(pam, pam_start, [PAM_LIB=yes], [], [])
AC_CHECK_LIB(pthread, pthread_create, [PTHREAD_LIB=yes], [], [])
AC_CHECK_HEADER(security/pam_appl.h, [PAM_HEADER=yes], [], [])
if test "x$PAM_LIB" = "x"; then
AC_MSG_ERROR([PAM library was not found])
fi
if test "x$PTHREAD_LIB" = "x"; then
AC_MSG_ERROR([pthread library was not found])
fi
if test "x$PAM_HEADER" = "x"; then
AC_MSG_ERROR([PAM header file "security/pam_appl.h" was not found])
fi
AC_OUTPUT
{erl_opts, [debug_info, {i, "include"}]}.
{port_env, [{"EXE_LDFLAGS", "$ERL_LDFLAGS -lpthread -lpam"}]}.
{port_specs, [{"priv/bin/epam", ["c_src/epam.c"]}]}.
%% Local Variables:
%% mode: erlang
%% End:
%% vim: set filetype=erlang tabstop=8:
%%%-------------------------------------------------------------------
%%% 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>
%%%
%%%
%%% epam, 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('ekhramtsov@process-one.net').
-behaviour(gen_server).
-include_lib("kernel/include/file.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 erlang user must "
"be able to read/execute it. You have "
"been warned :)~n").
-define(PROCNAME, ?MODULE).
-define(CMD_AUTH, 0).
-define(CMD_ACCT, 1).
-record(state, {port}).
start() ->
ChildSpec = {?PROCNAME, {?MODULE, start_link, []},
transient, 1000, worker, [?MODULE]},
supervisor:start_child(epam_sup, ChildSpec).
stop() ->
gen_server:call(?PROCNAME, stop),
supervisor:terminate_child(epam_sup, ?PROCNAME),
supervisor:delete_child(epam_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}).
init([]) ->
FileName = filename:join(get_bin_path(), "epam"),
case file:read_file_info(FileName) of
{ok, Info} ->
Mode = Info#file_info.mode band 2049,
if Mode == 2049 ->
error_logger:error_msg(?WARNING, [FileName]);
true -> ok
end,
Port = open_port({spawn, FileName},
[{packet, 2}, binary, exit_status]),
{ok, #state{port = Port}};
{error, Reason} ->
error_logger:error_msg("Can't open file ~p: ~p~n",
[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_logger:error_msg("Got invalid reply from ~p: ~p~n", [Port, Err])
end,
{noreply, State};
handle_info({Port, {exit_status, _}},
#state{port = Port} = State) ->
{stop, port_died, State};
handle_info(Msg, State) ->
error_logger:error_msg("got unexpected message: ~p~n", [Msg]),
{noreply, State}.
handle_cast(_Msg, State) -> {noreply, State}.
code_change(_OldVsn, State, _Extra) -> {ok, State}.
get_bin_path() ->
case os:getenv("EJABBERD_BIN_PATH") of
false ->
case code:priv_dir(p1_pam) of
{error, _} ->
filename:join(["priv", "bin"]);
Path ->
filename:join([Path, "bin"])
end;
Path ->
Path
end.
%%%-------------------------------------------------------------------
%%% @author Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%% @copyright (C) 2013, Evgeniy Khramtsov
%%% @doc
%%%
%%% @end
%%% Created : 4 Apr 2013 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%%-------------------------------------------------------------------
-module(epam_app).
-behaviour(application).
%% Application callbacks
-export([start/2, stop/1]).
%%%===================================================================
%%% Application callbacks
%%%===================================================================
%%--------------------------------------------------------------------
%% @private
%% @doc
%% This function is called whenever an application is started using
%% application:start/[1,2], and should start the processes of the
%% application. If the application is structured according to the OTP
%% design principles as a supervision tree, this means starting the
%% top supervisor of the tree.
%%
%% @spec start(StartType, StartArgs) -> {ok, Pid} |
%% {ok, Pid, State} |
%% {error, Reason}
%% StartType = normal | {takeover, Node} | {failover, Node}
%% StartArgs = term()
%% @end
%%--------------------------------------------------------------------
start(_StartType, _StartArgs) ->
case epam_sup:start_link() of
{ok, Pid} ->
{ok, Pid};
Error ->
Error
end.
%%--------------------------------------------------------------------
%% @private
%% @doc
%% This function is called whenever an application has stopped. It
%% is intended to be the opposite of Module:start/2 and should do
%% any necessary cleaning up. The return value is ignored.
%%
%% @spec stop(State) -> void()
%% @end
%%--------------------------------------------------------------------
stop(_State) ->
ok.
%%%===================================================================
%%% Internal functions
%%%===================================================================
%%%-------------------------------------------------------------------
%%% @author Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%% @copyright (C) 2013, Evgeniy Khramtsov
%%% @doc
%%%
%%% @end
%%% Created : 4 Apr 2013 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%%-------------------------------------------------------------------
-module(epam_sup).
-behaviour(supervisor).
%% API
-export([start_link/0]).
%% Supervisor callbacks
-export([init/1]).
-define(SERVER, ?MODULE).
%%%===================================================================
%%% API functions
%%%===================================================================
%%--------------------------------------------------------------------
%% @doc
%% Starts the supervisor
%%
%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}
%% @end
%%--------------------------------------------------------------------
start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
%%%===================================================================
%%% Supervisor callbacks
%%%===================================================================
%%--------------------------------------------------------------------
%% @private
%% @doc
%% Whenever a supervisor is started using supervisor:start_link/[2,3],
%% this function is called by the new process to find out about
%% restart strategy, maximum restart frequency and child
%% specifications.
%%
%% @spec init(Args) -> {ok, {SupFlags, [ChildSpec]}} |
%% ignore |
%% {error, Reason}
%% @end
%%--------------------------------------------------------------------
init([]) ->
EPAM = {epam, {epam, start_link, []},
permanent, brutal_kill, worker, [epam]},
{ok, {{one_for_one, 10, 1}, [EPAM]}}.
%%%===================================================================
%%% Internal functions
%%%===================================================================
%%%-------------------------------------------------------------------
%%% @author Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%% @copyright (C) 2013, Evgeniy Khramtsov
%%% @doc
%%%
%%% @end
%%% Created : 4 Apr 2013 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%%-------------------------------------------------------------------
{application, p1_pam,
[{description, "PAM wrapper"},
{vsn, "0.1.0"},
{modules, []},
{registered, []},
{applications, [kernel, stdlib]},
{mod, {epam_app,[]}}]}.
%% Local Variables:
%% mode: erlang
%% End:
%% vim: set filetype=erlang tabstop=8:
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment