ejabberd_auth_pam tested

add pam auth in ejabberd 1.1.1 and 1.1.2

base on http://www.ejabberd.im/pam patch dizzyd (http://www.dizzyd.com/mercurial/ejabberd 13 months ago and support old 0.9.8... pls... change Requirements on http://www.ejabberd.im/pam) and add new option

#patch -p0 < ejabberd_auth_pam.diff
patching file aclocal.m4
patching file configure.ac
patching file Makefile.in
patching file pam/ejabberd_auth_pam.c
patching file pam/ejabberd_auth_pam.erl
patching file pam/Makefile.in

autoconf, configure, make and install/reinstall

in ejabberd.cfg add:

%%% Add new auth method 
%{auth_method, [ internal, pam ] }.

%%% Default pam service "login" can be change 
%{pam_service, "login" }.
%%% Default pam prompt "Password:" can be change 
%{pam_prompt_pwd, "Password:" }.

Tested on redhat9 and ejabberd 1.1.2

ejabberd_auth_pam.diff

Quote:

Index: aclocal.m4
--- aclocal.m4 2006-09-28 04:49:59.000000000 +0800
+++ aclocal.m4 2006-12-11 14:15:12.000000000 +0700
@@ -292,3 +292,32 @@
fi
])
dnl
+
+AC_DEFUN(AM_WITH_PAM,
+[ AC_ARG_WITH(pam, [ --with-pam=PREFIX prefix where PAM is installed ])
+unset PAM_LIBS;
+unset PAM_CFLAGS;
+
+if test x"$with_pam" != x; then
+ PAM_CFLAGS="-I$with_pam/include"
+ PAM_LIBS="-L$with_pam/lib"
+fi
+
+AC_CHECK_LIB(pam, pam_start,
+ [ PAM_LIBS="$PAM_LIBS -lpam" pam_found=yes ],
+ [ pam_found=no ],
+ "$PAM_LIBS")
+
+if test $pam_found = yes; then
+ pam_save_CFLAGS="$CFLAGS"
+ CFLAGS="$PAM_CFLAGS $CFLAGS"
+ AC_CHECK_HEADERS(security/pam_appl.h, ,$pam_found=no)
+ if test $pam_found = no; then
+ AC_MSG_ERROR([Could not find security/pam_appl.h])
+ fi
+ CFLAGS=$pam_save_CFLAGS
+
+ AC_SUBST(PAM_CFLAGS)
+ AC_SUBST(PAM_LIBS)
+fi
+])
Index: configure.ac
--- configure.ac 2006-09-28 04:49:59.000000000 +0800
+++ configure.ac 2006-12-11 14:17:12.000000000 +0700
@@ -16,6 +16,10 @@
AM_WITH_EXPAT
#locating zlib
AM_WITH_ZLIB
+#locating zlib
+AM_WITH_ZLIB
+#locating Linux-PAM
+AM_WITH_PAM

# Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
@@ -27,6 +31,7 @@
AC_FUNC_MALLOC
AC_HEADER_STDC

+AC_MOD_ENABLE(pam, yes)
AC_MOD_ENABLE(mod_pubsub, yes)
AC_MOD_ENABLE(mod_irc, yes)
AC_MOD_ENABLE(mod_muc, yes)
@@ -55,6 +60,7 @@
AC_SUBST(db_type)

AC_CONFIG_FILES([Makefile
+ $make_pam
$make_mod_irc
$make_mod_muc
$make_mod_pubsub
Index: Makefile.in
--- Makefile.in 2006-09-28 04:49:59.000000000 +0800
+++ Makefile.in 2006-12-11 14:15:12.000000000 +0700
@@ -27,7 +27,7 @@

prefix = @prefix@

-SUBDIRS = @mod_irc@ @mod_pubsub@ @mod_muc@ @eldap@ @web@ stringprep @tls@ @odbc@ @ejabberd_zlib@
+SUBDIRS = @mod_irc@ @mod_pubsub@ @mod_muc@ @eldap@ @web@ stringprep @tls@ @odbc@ @ejabberd_zlib@ @pam@
ERLSHLIBS = expat_erl.so
SOURCES = $(wildcard *.erl)
BEAMS = $(SOURCES:.erl=.beam)
Index: pam/ejabberd_auth_pam.c
--- pam/ejabberd_auth_pam.c 1970-01-01 07:00:00.000000000 +0700
+++ pam/ejabberd_auth_pam.c 2006-12-10 18:11:27.000000000 +0700
@@ -0,0 +1,255 @@
+#include
+#include
+#include
+#include
+#include
+
+#ifdef LINUX_PAM /* XXX: and OpenPAM? */
+# define PAM_MSG_MEMBER(msg, n, member) ((msg)[(n)]->member)
+#else
+# define PAM_MSG_MEMBER(msg, n, member) ((*(msg))[(n)].member)
+#endif
+
+//
+#ifndef LINKEDLIBPAM
+#include
+
+#ifndef LIBPAMPATH
+#define LIBPAMPATH "/usr/lib/libpam.so"
+#endif
+
+static void *libpam_h = NULL;
+
+void init_libpam() {
+ if ( libpam_h == NULL ) {
+ libpam_h = dlopen( LIBPAMPATH, RTLD_GLOBAL | RTLD_NOW );
+ printf( "Open: %s\r\n", LIBPAMPATH );
+ };
+};
+
+void fin_libpam() {
+ if ( libpam_h != NULL ) {
+ dlclose( libpam_h );
+ libpam_h = NULL;
+ printf( "Close: %s\r\n", LIBPAMPATH );
+ };
+};
+#endif
+//
+
+typedef struct _PromptMatch {
+ struct _PromptMatch* next;
+ char* prompt;
+ char* response;
+} *PromptMatch;
+
+typedef struct {
+ ErlDrvPort port;
+} ejabberd_pam_data;
+
+static PromptMatch _newMatch( PromptMatch list, char* prompt, char* response ) {
+ PromptMatch result = ( PromptMatch)malloc( sizeof( struct _PromptMatch ));
+ bzero( result, sizeof( struct _PromptMatch ));
+ result->prompt = prompt;
+ result->response = response;
+ result->next = list;
+ return result;
+};
+
+static char* _findMatch( PromptMatch list, const char* prompt ) {
+ PromptMatch cur = list;
+ while ( cur != NULL ) {
+ if ( strstr( prompt, cur->prompt ) != 0 )
+ return cur->response;
+ cur = cur->next;
+ };
+ return NULL;
+};
+
+static void _cleanupMatch( PromptMatch list ) {
+ PromptMatch cur = list;
+ PromptMatch next;
+ while ( cur != NULL ) {
+ next = cur->next;
+ free( cur->prompt );
+ free( cur->response );
+ free( cur );
+ cur = next;
+ };
+};
+
+static int pam_conversation( int msgcnt, const struct pam_message** msgs, struct pam_response** res, void* arg ) {
+ int iResult = PAM_CONV_ERR;
+ if (( msgcnt > 0 ) || ( msgcnt <= PAM_MAX_NUM_MSG )) {
+ // Allocate a response for each message
+ int iSizeResponse = sizeof( struct pam_response ) * msgcnt;
+ struct pam_response *reply = (struct pam_response *)malloc( iSizeResponse );
+ if ( reply != NULL ) {
+ bzero( reply, iSizeResponse );
+ // Walk each message from PAM and lookup the response corresponding to each prompt
+ int ii;
+ char* response;
+ for ( ii = 0; ii < msgcnt; ii++ ) {
+ printf( "prompt(index %d)(style %d): %s\r\n", ii, PAM_MSG_MEMBER( msgs, ii, msg_style ), PAM_MSG_MEMBER( msgs, ii, msg ));
+ switch ( PAM_MSG_MEMBER( msgs, ii, msg_style )) {
+ case PAM_PROMPT_ECHO_ON :
+ case PAM_PROMPT_ECHO_OFF :
+ // Find the response for the current prompt; if none is found, warn
+ response = _findMatch( (PromptMatch)arg, PAM_MSG_MEMBER( msgs, ii, msg ));
+ if ( response != NULL ) {
+ // Copy password into response
+ reply[ ii ].resp = strdup( response );
+ reply[ ii ].resp_retcode = PAM_SUCCESS;
+ } else {
+ printf("Warning: No prompt found for: %s\r\n", PAM_MSG_MEMBER( msgs, ii, msg ));
+ reply[ ii ].resp = strdup( "Unexpected prompt" );
+ reply[ ii ].resp_retcode = PAM_SUCCESS;
+ };
+ break;
+ default :
+ reply[ ii ].resp = strdup("");
+ reply[ ii ].resp_retcode = PAM_SUCCESS;
+ };
+ };
+ *res = reply;
+ iResult = PAM_SUCCESS;
+ };
+ };
+ return iResult;
+};
+
+#define DECODE_STRING(buffer, index, result) \
+ { \
+ int tmp = 0; int size = 0; \
+ ei_get_type(buffer, index, &tmp, &size); \
+ result = malloc(size + 1); \
+ ei_decode_string(buffer, index, result); \
+ }
+
+#define RETURN_INT(value) \
+ { \
+ ErlDrvBinary* b = driver_alloc_binary(1); \
+ rlen = 1; \
+ b->orig_bytes[0] = value; \
+ *rbuf = (char*)b; \
+ return rlen; \
+ }
+
+
+static ErlDrvData start( ErlDrvPort port, char* command ) {
+ printf( "PAM-AUTH driver start\r\n" );
+ #ifndef LINKEDLIBPAM
+ init_libpam();
+ #endif
+ ejabberd_pam_data *d = (ejabberd_pam_data *)driver_alloc( sizeof( ejabberd_pam_data ));
+ d->port = port;
+ set_port_control_flags( port, PORT_CONTROL_FLAG_BINARY );
+ return (ErlDrvData)d;
+};
+
+static void stop( ErlDrvData handle ) {
+ driver_free( (char *)handle );
+ #ifndef LINKEDLIBPAM
+ fin_libpam();
+ #endif
+ printf( "PAM-AUTH driver stop\r\n" );
+};
+
+static int auth_params( char* buf, PromptMatch* head ) {
+ int iSize = 0;
+ int iIndex = 0;
+ int iResult = 0;
+ char* prompt = NULL;
+ char* response = NULL;
+ // Parse the version and tuple header
+ ei_decode_version( buf, &iIndex, &iSize );
+ ei_decode_tuple_header( buf, &iIndex, &iSize );
+ if ( iSize == 2 ) {
+ // The first item in the tuple should be the username
+ DECODE_STRING( buf, &iIndex, response );
+ *head = _newMatch( *head, strdup( "login" ), response );
+
+ // The second item in the tuple should be a list of tuples
+ // Each tuple in the list consists of the expected prompt string
+ // and the value to return at that prompt
+ ei_decode_list_header( buf, &iIndex, &iSize );
+
+ // Loop over the list and pull out each prompt/response tuple and
+ // store it into a PromptItem list that will get processed in
+ // the PAM conversation
+ int ii = 0;
+ for ( ii = 0; ii < iSize; ii++ ) {
+ int iTupleSize = 0;
+ ei_decode_tuple_header( buf, &iIndex, &iTupleSize );
+ if ( iTupleSize != 2 ) {
+ printf( "Incorrect number of arguments in prompt/response tuple(index %d): %d\r\n", ii, iTupleSize );
+ } else {
+ // Decode the prompt from the tuple
+ DECODE_STRING( buf, &iIndex, prompt );
+ DECODE_STRING( buf, &iIndex, response );
+ // Add a new prompt item to track this prompt/response pair
+ *head = _newMatch( *head, prompt, response );
+ };
+ };
+ iResult = 1;
+ };
+ return iResult;
+};
+
+static int auth( PromptMatch head ) {
+ // Setup PAM conversation structure; pass in the prompt match list
+ // so we can process the prompts from PAM
+ int flags = 0; //PAM_SILENT
+ int iResult = 0;
+ pam_handle_t* pam = NULL;
+
+ char* service = _findMatch( head, "service" );
+ char* username = _findMatch( head, "login" );
+
+ struct pam_conv conv = { pam_conversation, head };
+
+ // Spin up PAM
+ int rc = pam_start( service, username, &conv, &pam );
+ if ( rc == PAM_SUCCESS ) {
+ // Attempt to authenticate the user
+ rc = pam_authenticate( pam, flags );
+ if ( rc != PAM_SUCCESS )
+ printf("Authentication failed for: %s. Error: %s\r\n", username, pam_strerror( NULL, rc ));
+ else
+ iResult = 1;
+ pam_end( pam, rc );
+ } else
+ printf("Unable to spin up PAM: %s\r\n", pam_strerror( NULL, rc ));
+ return iResult;
+};
+
+static int control( ErlDrvData drv_data, unsigned int command, char *buf,
+ int len, char** rbuf, int rlen ) {
+ int iResult;
+ PromptMatch head = NULL;
+ if (( iResult = auth_params( buf, &head )) != 0 ) {
+ iResult = auth( head );
+ _cleanupMatch( head );
+ };
+ RETURN_INT( iResult );
+};
+
+static ErlDrvEntry entry = {
+ NULL, /* F_PTR init, N/A */
+ start, /* L_PTR start, called when port is opened */
+ stop, /* F_PTR stop, called when port is closed */
+ NULL, /* F_PTR output, called when erlang has sent */
+ NULL, /* F_PTR ready_input, called when input descriptor ready */
+ NULL, /* F_PTR ready_output, called when output descriptor ready */
+ "ejabberd_auth_pam", /* char *driver_name, the argument to open_port */
+ NULL, /* F_PTR finish, called when unloaded */
+ NULL, /* handle */
+ control, /* F_PTR control, port_command callback */
+ NULL, /* F_PTR timeout, reserved */
+ NULL /* F_PTR outputv, reserved */
+};
+
+DRIVER_INIT(pam_drv) /* must match name in driver_entry */
+{
+ return &entry;
+};
Index: pam/ejabberd_auth_pam.erl
--- pam/ejabberd_auth_pam.erl 1970-01-01 07:00:00.000000000 +0700
+++ pam/ejabberd_auth_pam.erl 2006-12-10 18:18:44.000000000 +0700
@@ -0,0 +1,78 @@
+-module(ejabberd_auth_pam).
+-author('').
+
+-export([
+ start/1,
+ plain_password_required/0,
+ set_password/3,
+ try_register/3,
+ remove_user/2,
+ remove_user/3,
+ get_password/2,
+ get_password_s/2,
+ is_user_exists/2,
+ get_vh_registered_users/1,
+ check_password/3,
+ check_password/5
+ ]).
+
+start( _Host ) ->
+ case erl_ddll:load_driver( ejabberd:get_so_path(), ejabberd_auth_pam) of
+ ok -> ok;
+ {error, already_loaded} -> ok;
+ Error -> exit({error, could_not_load_driver, Error})
+ end.
+
+plain_password_required() ->
+ true.
+
+check_password( User, Server, Password ) ->
+ PAMService =
+ case ejabberd_config:get_local_option( { pam_service, Server } ) of
+ undefined ->
+ "login";
+ Service ->
+ Service
+ end,
+ PAMPrompt =
+ case ejabberd_config:get_local_option( { pam_prompt_pwd, Server } ) of
+ undefined ->
+ "Password:";
+ Prompt ->
+ Prompt
+ end,
+ Port = open_port( { spawn, ejabberd_auth_pam }, [ binary ] ),
+ Bin = term_to_binary( { User, [ { PAMPrompt, Password }, { "service", PAMService } ] }),
+ Res = port_control( Port, 1, Bin ),
+ case Res of
+ <<0>> -> false;
+ <<1>> -> true;
+ _ -> undefined
+ end.
+
+check_password( User, Server, Password, _StreamID, _Digest ) ->
+ check_password( User, Server, Password ).
+
+set_password( _User, _Server, _Password ) ->
+ { error, not_allowed }.
+
+try_register( _User, _Server, _Password ) ->
+ { error, not_allowed }.
+
+remove_user( _User, _Server ) ->
+ { error, not_allowed }.
+
+remove_user( _User, _Server, _Password ) ->
+ { error, not_allowed }.
+
+get_vh_registered_users( _Server ) ->
+ [].
+
+is_user_exists( _User, _Server ) ->
+ false.
+
+get_password( _User, _Server ) ->
+ false.
+
+get_password_s( _User, _Server ) ->
+ "".
Index: pam/Makefile.in
--- pam/Makefile.in 1970-01-01 07:00:00.000000000 +0700
+++ pam/Makefile.in 2006-12-11 14:20:45.000000000 +0700
@@ -0,0 +1,35 @@
+# $Id: Makefile.in 285 2006-12-11 10:16:21Z $
+
+CC = @CC@
+CFLAGS = @CFLAGS@ @PAM_CFLAGS@ @ERLANG_CFLAGS@
+CPPFLAGS = @CPPFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBS = @LIBS@ @PAM_LIBS@ @ERLANG_LIBS@
+
+SUBDIRS =
+
+ERLSHLIBS = ../ejabberd_auth_pam.so
+
+OUTDIR = ..
+EFLAGS = -I .. -pz ..
+OBJS = \
+ $(OUTDIR)/ejabberd_auth_pam.beam
+
+all: $(OBJS) $(ERLSHLIBS)
+
+$(OUTDIR)/%.beam: %.erl
+ @ERLC@ -W $(EFLAGS) -o $(OUTDIR) $<
+
+$(ERLSHLIBS): ../%.so: %.c
+ $(CC) -Wall $(CFLAGS) $(LDFLAGS) \
+ $(subst ../,,$(subst .so,.c,$@)) $(LIBS) \
+ -o $@ -fpic -shared
+
+clean:
+ rm -f $(OBJS) $(ERLSHLIBS)
+
+distclean: clean
+ rm -f Makefile
+
+TAGS:
+ etags *.erl

Thanks, I've uploaded your

Thanks, I've uploaded your patch and modified the text.

Ok! thank you too... tested

Ok! thank you too... tested pam-mod on freebsd6.1 + ejabberd 1.1.2... its worked

Syndicate content